Giter VIP home page Giter VIP logo

val-loader's Introduction

npm node tests coverage discussion size

val-loader

A webpack loader which executes a given module, and returns the result of the execution at build-time, when the module is required in the bundle. In this way, the loader changes a module from code to a result.

Another way to view val-loader, is that it allows a user a way to make their own custom loader logic, without having to write a custom loader.

The target module is called with two arguments: (options, loaderContext)

  • options: The loader options (for instance provided in the webpack config. See the example below).
  • loaderContext: The loader context.

Getting Started

To begin, you'll need to install val-loader:

npm install val-loader --save-dev
yarn add -D val-loader
pnpm add -D val-loader

Then add the loader to your webpack config. For example:

target-file.js

module.exports = (options, loaderContext) => {
  return { code: "module.exports = 42;" };
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /target-file.js$/,
        use: [
          {
            loader: `val-loader`,
          },
        ],
      },
    ],
  },
};

src/entry.js

const answer = require("target-file");

And run webpack via your preferred method.

Options

executableFile

Type:

type executableFile = string;

Default: undefined

Allows to specify path to the executable file

data.json

{
  "years": "10"
}

executable-file.js

module.exports = function yearsInMs(options, loaderContext, content) {
  const { years } = JSON.parse(content);
  const value = years * 365 * 24 * 60 * 60 * 1000;

  return {
    cacheable: true,
    code: "module.exports = " + value,
  };
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(json)$/i,
        rules: [
          {
            loader: "val-loader",
            options: {
              executableFile: path.resolve(
                __dirname,
                "fixtures",
                "executableFile.js",
              ),
            },
          },
        ],
      },
      {
        test: /\.json$/i,
        type: "asset/resource",
      },
    ],
  },
};

Return Object Properties

Targeted modules of this loader must export a Function that returns an object, or a Promise resolving an object (e.g. async function), containing a code property at a minimum, but can contain any number of additional properties.

code

Type:

type code = string | Buffer;

Default: undefined Required

Code passed along to webpack or the next loader that will replace the module.

sourceMap

Type:

type sourceMap = object;

Default: undefined

A source map passed along to webpack or the next loader.

ast

Type:

type ast = Array<object>;

Default: undefined

An Abstract Syntax Tree that will be passed to the next loader. Useful to speed up the build time if the next loader uses the same AST.

dependencies

Type:

type dependencies = Array<string>;

Default: []

An array of absolute, native paths to file dependencies that should be watched by webpack for changes.

Dependencies can also be added using loaderContext.addDependency(file: string).

contextDependencies

Type:

type contextDependencies = Array<string>;

Default: []

An array of absolute, native paths to directory dependencies that should be watched by webpack for changes.

Context dependencies can also be added using loaderContext.addContextDependency(directory: string).

buildDependencies

Type:

type buildDependencies = Array<string>;

Default: []

An array of absolute, native paths to directory dependencies that should be watched by webpack for changes.

Build dependencies can also be added using loaderContext.addBuildDependency(file: string).

cacheable

Type:

type cacheable = boolean;

Default: false

If true, specifies that the code can be re-used in watch mode if none of the dependencies have changed.

Examples

Simple

In this example the loader is configured to operator on a file name of years-in-ms.js, execute the code, and store the result in the bundle as the result of the execution. This example passes years as an option, which corresponds to the years parameter in the target module exported function:

years-in-ms.js

module.exports = function yearsInMs({ years }) {
  const value = years * 365 * 24 * 60 * 60 * 1000;

  // NOTE: this return value will replace the module in the bundle
  return {
    cacheable: true,
    code: "module.exports = " + value,
  };
};

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: require.resolve("src/years-in-ms.js"),
        use: [
          {
            loader: "val-loader",
            options: {
              years: 10,
            },
          },
        ],
      },
    ],
  },
};

In the bundle, requiring the module then returns:

import tenYearsMs from "years-in-ms";

console.log(tenYearsMs); // 315360000000

Modernizr

Example shows how to build modernizr.

entry.js

import modenizr from "./modernizr.js";

modernizr.js

const modernizr = require("modernizr");

module.exports = function (options) {
  return new Promise(function (resolve) {
    // It is impossible to throw an error because modernizr causes the process.exit(1)
    modernizr.build(options, function (output) {
      resolve({
        cacheable: true,
        code: `var modernizr; var hadGlobal = 'Modernizr' in window; var oldGlobal = window.Modernizr; ${output} modernizr = window.Modernizr; if (hadGlobal) { window.Modernizr = oldGlobal; } else { delete window.Modernizr; } export default modernizr;`,
      });
    });
  });
};

webpack.config.js

const path = require("path");
module.exports = {
  module: {
    rules: [
      {
        test: path.resolve(__dirname, "src", "modernizr.js"),
        use: [
          {
            loader: "val-loader",
            options: {
              minify: false,
              options: ["setClasses"],
              "feature-detects": [
                "test/css/flexbox",
                "test/es6/promises",
                "test/serviceworker",
              ],
            },
          },
        ],
      },
    ],
  },
};

Figlet

Example shows how to build figlet.

entry.js

import { default as figlet } from "./figlet.js";

console.log(figlet);

figlet.js

const figlet = require("figlet");

function wrapOutput(output, config) {
  let figletOutput = "";

  if (config.textBefore) {
    figletOutput += encodeURI(`${config.textBefore}\n`);
  }

  output.split("\n").forEach((line) => {
    figletOutput += encodeURI(`${line}\n`);
  });

  if (config.textAfter) {
    figletOutput += encodeURI(`${config.textAfter}\n`);
  }

  return `module.exports = decodeURI("${figletOutput}");`;
}

module.exports = function (options) {
  const defaultConfig = {
    fontOptions: {
      font: "ANSI Shadow",
      horizontalLayout: "default",
      kerning: "default",
      verticalLayout: "default",
    },
    text: "FIGLET-LOADER",
    textAfter: null,
    textBefore: null,
  };

  const config = Object.assign({}, defaultConfig, options);

  return new Promise(function (resolve, reject) {
    figlet.text(config.text, config.fontOptions, (error, output) => {
      if (error) {
        return reject(error);
      }

      resolve({
        cacheable: true,
        code: "module.exports = " + wrapOutput(output, config),
      });
    });
  });
};

webpack.config.js

const path = require("path");
module.exports = {
  module: {
    rules: [
      {
        test: path.resolve(__dirname, "src", "figlet.js"),
        use: [
          {
            loader: "val-loader",
            options: {
              text: "FIGLET",
            },
          },
        ],
      },
    ],
  },
};

Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.

CONTRIBUTING

License

MIT

val-loader's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

val-loader's Issues

Trouble using val-loader and babel-loader with Babel 7

On my project, I compile all *.val.js files with val-loader and then babel-loader. It seems that Babel 7 is a little stricter with passing arguments that having null for the sourceMap won't work.

Minimal reproduction project: https://github.com/ngyikp/webpack-val-loader-babel-7-bug

OS: macOS 10.13.2
Node version: v8.9.3
npm version: 5.6.0

With Babel 6:

$ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.

$ npm install
npm WARN webpack-val-loader-babel-7-bug No description
npm WARN webpack-val-loader-babel-7-bug No repository field.

added 76 packages, removed 93 packages and updated 1 package in 4.218s

$ npm start

> @ start /Users/ngyikp/Code/webpack-val-loader-babel-7-bug
> webpack

Hash: 5a0a3576cd24ce04c09a
Version: webpack 3.10.0
Time: 535ms
  Asset  Size  Chunks             Chunk Names
main.js  3 kB       0  [emitted]  main
   [0] ./src/app.js 91 bytes {0} [built]
   [1] ./src/custom.val.js 52 bytes {0} [built]

With Babel 7:

$ git checkout babel-7
Switched to branch 'babel-7'
Your branch is up to date with 'origin/babel-7'.

$ npm install
npm WARN webpack-val-loader-babel-7-bug No description
npm WARN webpack-val-loader-babel-7-bug No repository field.

added 93 packages, removed 76 packages and updated 1 package in 3.818s

$ npm start

> @ start /Users/ngyikp/Code/webpack-val-loader-babel-7-bug
> webpack

Hash: eca466c3ce5d118691f2
Version: webpack 3.10.0
Time: 644ms
  Asset     Size  Chunks             Chunk Names
main.js  4.16 kB       0  [emitted]  main
   [0] ./src/app.js 89 bytes {0} [built]
   [1] ./src/custom.val.js 1.17 kB {0} [built] [failed] [1 error]

ERROR in ./src/custom.val.js
Module build failed: Error: .inputSourceMap must be a boolean, object, or undefined
    at assertInputSourceMap (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/config/validation/option-assertions.js:41:11)
    at /Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/config/validation/options.js:71:20
    at Array.forEach (<anonymous>)
    at validate (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/config/validation/options.js:57:21)
    at loadConfig (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/config/index.js:37:48)
    at transformSync (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/transform-sync.js:13:36)
    at Object.transform (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/@babel/core/lib/transform.js:20:65)
    at transpile (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/babel-loader/lib/index.js:55:20)
    at Object.module.exports (/Users/ngyikp/Code/webpack-val-loader-babel-7-bug/node_modules/babel-loader/lib/index.js:179:20)
 @ ./src/app.js 1:0-38
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! @ start: `webpack`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the @ start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/ngyikp/.npm/_logs/2017-12-30T15_15_36_863Z-debug.log

The problem is this line:

loaderContext.callback(null, result.code, result.sourceMap || null, result.ast || null);

I changed that line on my local patch to this:

loaderContext.callback(null, result.code, result.sourceMap || undefined, result.ast || null);

And it seems to work fine for me, but I'm not sure if it's breaking.

ESM target file support

Feature Proposal

Feature Use Case

After careful searching of the web, it seems that val-loader does not support target files expressed as esm. What do you think about adding this ability? In short, it would allow us to express target files as such:

export default (options, loaderContext) => {
  return { code: "module.exports = 42;" };
};

Right now it fails with the following error Unable to execute "<the file name>": SyntaxError: Unexpected token 'export'

I tried to find if any related work has been done on this before submitting this request and the closest seems to be this issue #13 for reference

Please paste the results of npx webpack-cli info here, and mention other relevant information

System:
OS: Linux 4.19 Ubuntu 20.04.1 LTS (Focal Fossa)
CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Memory: 1.49 GB / 7.72 GB
Binaries:
Node: 16.13.0 - ~/.nvm/versions/node/v16.13.0/bin/node
Yarn: 3.1.0 - ~/.nvm/versions/node/v16.13.0/bin/yarn
npm: 8.1.0 - ~/.nvm/versions/node/v16.13.0/bin/npm
Packages:
css-loader: ^6.5.0 => 6.5.0
postcss-loader: ^6.1.1 => 6.2.0
sass-loader: ^12.3.0 => 12.3.0
style-loader: ^3.3.1 => 3.3.1
val-loader: ^4.0.0 => 4.0.0
webpack: ^5.60.0 => 5.60.0

Specifing the script name in the options

Feature Proposal

Currently val-loader take the name of the script to run from require argument directly and run it.
Would it be possible if the script name was specified in options inside webpack.config and require argument is passed to the script as a parameter as follows:

code-generator.js

module.exports = (options, loaderContext) => {
  return { code: generate_code_from_json(options.input_data) };
};

webpack.config.js

      {
        test: /\.(xx)\.(json)$/,
        use: {
          loader:'val-loader',
          options: {
            script_name: 'code-generator.js',
          }
        },
      },

main.js

const data1 = require('raw1.xx.json');
const data2 = require('raw2.xx.json');

Feature Use Case

It is common in meta-programming to convert data into code where the conversion process is same for multiple sets of data. Currently the way to achieve this is to create wrapper js file for each data file

Let say there are multiple static articles each stored in a separate JSON file and I need to use it in the code. I could import this file using json-loader and convert it into HTML at runtime.
However, it would be better if I can use val-loader instead and keep the require line as it is and specify the name of a script file that will convert the JSON file into HTML.

Inline evaluations

  • Operating System: Windows
  • Node Version: 8.9.2
  • NPM Version: 6.4.0
  • webpack Version: 4x
  • val-loader Version: 1.0.1

This issue is for a:

  • bug
  • feature request
  • modification request

New Feature Use Case

Hello,

Would it be possible to implement a version closer to babel-plugin-preval

It would be great to be able to use preval directly into other code without having to split code in multiple files and in-lining the result.
It could be of great use when using angular AOT which have a lots of restriction.

Thanks a lot.

support async code.

like this.

module.exports = async () => {
  const code = await fetchCode()
  return { code }
};

TypeError: this.getOptions is not a function

  • Operating System: macOS 10.15.7
  • Node Version: 14.15.0
  • NPM Version: 6.14.8
  • webpack Version: 4.44.2
  • val-loader Version: 3.0.0

Expected Behavior

processed.js gets evaluated correctly.

Actual Behavior

Module build failed (from ./node_modules/val-loader/dist/cjs.js):
TypeError: this.getOptions is not a function
    at Object.loader (<redacted>/node_modules/val-loader/dist/index.js:51:24)

This was modified in https://github.com/webpack-contrib/val-loader/pull/61/files#diff-bfe9874d239014961b1ae4e89875a6155667db834a410aaaa2ebe3cf89820556R67.

Code

// processed.js
module.exports = () => {
    return {
        code: `module.exports = 32;`
    }
}
// main.js
require('val-loader!./processed.js');

How Do We Reproduce?

webpack --entry main.js

Misleading README Promise usage

  • Operating System:
  • Node Version:
  • NPM Version:
  • webpack Version:
  • val-loader Version:

This issue is for a:

  • bug
  • feature request
  • modification request
  • documentation

Return Object Properties

Targeted modules of this loader must export either a Function or Promise that returns an object containing a code property at a minimum, but can contain any number of additional properties.

The part "export either a Function or Promise that" seems misleading.While checking loader source code I noticed that it expects function that returns object code or Promise. As I suspected passing returning promise in module export generates errors

Cannot use `resourceQuery` with cache

Bug report

It conflicts with webpack cache

Actual Behavior

[webpack.cache.PackFileCacheStrategy] Caching failed for pack: Error: ENOENT: no such file or directory, lstat './val-loader-resource-query-bug/val-entry.js?entry=foo'

Expected Behavior

How Do We Reproduce?

const path = require('path')

/**
 * @type {import('webpack').Configuration}
 */
module.exports = {
  mode: 'development',
  entry: {
    main: './val-entry.js?entry=foo',
  },
  cache: {
    type: 'filesystem',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /val-entry\.js$/,
        use: [{loader: `val-loader`}],
      },
    ],
  },
}

See https://github.com/ambar/val-loader-resource-query-bug

Please paste the results of npx webpack-cli info here, and mention other relevant information

➜  val-loader-resource-query-bug git:(main) npx webpack-cli info

  System:
    OS: macOS 11.7
    CPU: (4) x64 Intel(R) Core(TM) i5-4690 CPU @ 3.50GHz
    Memory: 150.00 MB / 24.00 GB
  Binaries:
    Node: 16.14.2 - ~/.nvm/versions/node/v16.14.2/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 8.5.0 - ~/.nvm/versions/node/v16.14.2/bin/npm
  Browsers:
    Chrome Canary: 110.0.5436.0
    Firefox: 105.0.3
    Safari Technology Preview: 15.4
  Packages:
    val-loader: ^5.0.0 => 5.0.0 
    webpack: ^5.75.0 => 5.75.0 
    webpack-cli: ^5.0.0 => 5.0.0 

Question: How to pass parametes to the node.js-runnable part of tthe file

here is my file which i require with require(val!./myfile)

var foo = "hello world";
console.log(foo);

// this part of code runs in the node.js environment itself before the webpack compilation

// then we have this

module.exports = "function() {
// doing something
}"

All string that goes to the module.exports will included as required file, and then webpack compiled.

So my question is - is it possible to do something like this
require(val!./myfile)(params) or maybe require(val?params={...}!./myfile)
and get the params variable inside node.js context - something like this

var foo = "hello world";

console.log(params)
var exportString = "module.exports = 'my string' " + params
console.log(foo);

// this part of code above runs in the node.js environment itself before the webpack compilation
module.exports = exportString

Webpack 4 compatibility

  • Operating System: MacOS 10.13.4
  • Node Version: v8.11.0
  • NPM Version: 6.0.0
  • webpack Version: 4.8.1
  • val-loader Version: 1.1.0

This issue is for a:

  • bug
  • feature request
  • modification request

Code

Expected Behavior

val-loader would install without complaining if webpack >= 4 was used.

Actual Behavior

npm complains that val-loader requires webpack ^2.0.0 || ^3.0.0 but runs just fine.

What is the most convenient way to return object with val-loader?

I've successfully got an object in example below.

// module1.js: (Client-side)
var r = require('exports?json!val!./module2.js');
console.log(r);
// module2.js: (Server-side)
var path = require('path');
var folder = path.basename(__dirname);
var name = other_function();
var json = JSON.stringify({
    folder: folder,
    name: name
});
module.exports = 'var json = ' + json + ';';

It would be nice, if it was possible to return an object directly. I have tried to do in example below. But it gives an error. How can i do this?

// module1.js: (Client-side)
var r = require('val!./module2.js');
console.log(r);
// module2.js: (Server-side)
var path = require('path');
var folder = path.basename(__dirname);
var name = some_node_module();
module.exports = {
    folder: folder,
    name: name
};

webpack-defaults upgrade

Addition of webpack-defaults & associated refactoring as a part of the next Major release

Issue exists for status tracking across the organization.

Please do not close

Documentation is Apalling

It's extremely difficult to find any decent explanation of what this loader does or how it works, or even a couple examples.

provide script with loader environment and post-process result

It would be great if the script gets the same this as loaders do, so that a generator-type of script can mark itself cacheable etc.

Then, it would be even more awesome if there was a way to name the resulting file, so that the regular loaders can process it.

So for example, using the entry val?name=resources.css!./resources/generate-css, val-loader would execute the generate-css script as a loader with empty input, and the result would be processed by the loaders that match ./resources/resources.css.

ES2015 modules does not supported by current node.js, but present in dist

node.js: 7.7.3

dist unsupported code

import path from 'path';
import loaderUtils from 'loader-utils';

...

export default valLoader;
C:\12341234>npm i val-loader
C:\12341234
`-- [email protected]
  `-- [email protected]
    +-- [email protected]
    +-- [email protected]
    `-- [email protected]

npm WARN enoent ENOENT: no such file or directory, open 'C:\12341234\package.json'
npm WARN 12341234 No description
npm WARN 12341234 No repository field.
npm WARN 12341234 No README data
npm WARN 12341234 No license field.

C:\12341234>node
> require("val-loader")
C:\12341234\node_modules\val-loader\dist\index.js:1
(function (exports, require, module, __filename, __dirname) { import path from 'path';
                                                              ^^^^^^
SyntaxError: Unexpected token import


C:\12341234>node -v
v7.7.3

Need a way to output errors / emitError

  • Operating System: Ubuntu 18.04
  • Node Version: 10.16.3
  • NPM Version: 6.13.0
  • webpack Version: 4.37.0
  • val-loader Version: 2.0.2

Feature Proposal

Provide the exported function with the loaderContext so that this.emitError can be called from within it.
e.g. in index.js#L96:

- result = func(options)
+ result = func.call(this, options)

Goes well with what is stated in the README:

it allows a user a way to make their own custom loader logic, without having to write a custom loader

Feature Use Case

I'm using val-loader as an entry file in webpack to parse some files before generating code based on the parsed files. These files are added as dependencies (in return statement) so that webpack watches them for changes.

I want to emit an error to indicate errors in those files.

Using throw interrupts the compilation process, and the dependencies are not added. After fixing the errors, the compiler is not run again, since webpack is not watching the files (dependencies not returned because an exception was thrown).

If I have access to the loader context I can either:

  • use this.emitError instead of throw, and I'll be able to return the dependencies (and partially generated code?)
  • use this.addDependency before parsing so that when an exception is thrown, the dependency is known to webpack.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.