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.To begin, you'll need to install val-loader
:
$ npm install val-loader --save-dev
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.
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: String|Buffer
Default: undefined
Required
Code passed along to webpack or the next loader that will replace the module.
sourceMap
Type: Object
Default: undefined
A source map passed along to webpack or the next loader.
ast
Type: 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: 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: 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)
.
cacheable
Type: Boolean
Default: false
If true
, specifies that the code can be re-used in watch mode if none of the
dependencies
have changed.
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
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',
],
},
},
],
},
],
},
};
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',
},
},
],
},
],
},
};
Please take a moment to read our contributing guidelines if you haven't yet done so.