-
Notifications
You must be signed in to change notification settings - Fork 128
loaders
Loaders are transformations that are applied on files. They preprocess files. I. e. they can transform coffeescript to javascript.
Loaders resolve similar to modules. You can apply loaders to modules by prefixing them in the require
call:
var moduleWithOneLoader = require("loader!module");
// Loader in current directory
require("./loader.js!module");
// multiple loaders
require("loader1/main!./loader2!module");
When using multiple loaders they are applied in a pipeline.
Loaders accept query parameters:
require("loader?with=parameter!./file");
The format of the query string is up to the loader, so check the loaders documentation to find out about the parameters the loader accept.
In many cases the correct loader can be inferred from the filename. Therefore loaders can be specified in the configuration:
{
module: {
loaders: [
{ test: /\.coffee$/, loader: "coffee-loader" }
],
preLoaders: [
{ test: /\.coffee$/, loader: "coffee-hint-loader" }
]
}
};
If the loaders from the configuration is not suitable for a specific case it can be overwritten:
require("raw!./script.coffee"); // loaders, preLoaders and postLoaders are applied
// => coffee-loader ! raw ! coffee-hint-loader ! ./script.coffee
require("!raw!./script.coffee"); // only preLoaders and postLoaders are applied
// => raw ! coffee-hint-loader ! ./script.coffee
require("!!raw!./script.coffee"); // no loaders from configuration are applied
// => raw ! ./script.coffee
// The last case should only be used in generated code (generated by loaders)
- The file is read from filesystem
-
options.module.preLoaders
are applied -
options.module.loaders
are applied - loaders from request are applied
-
options.module.postLoaders
are applied - done
If request matches /^!/
omit step 3.
If request matches /^!!/
omit step 2., 3. and 5.
If request matches /^-!/
omit step 2. and 3.
It is recommended that the result is javascript after step 3.
It is recommended to applied non-js to js transformations in step 2 (or step 3 when they don't apply globally).
It is recommended to stay in the same language in pre and post loaders.
Source code that want to override the non-js to js transformation should use the !
prefix. (i. e. to transform it in another way)
The !!
prefix and the -!
prefix should only be used by loaders.
- Example for a preLoader: Image compression
- Example for a loader (in config): coffee-script transformation
- Example for a loader (in request): bundle loader
- Example for a postLoader: Code coverage instrumenting
Writing a loader is pretty simple. A loader is just a file, which exports a function. That function is called by the compiler. The result of the previous loader is passed to the next loader. The this
context is filled by the compiler with some useful methods. I. e. loaders can change there invocation style to async or get query parameters. The first loader is passed one argument: the content of the resource file. The compiler expect a result from the last loader. The first result should be a String or a Buffer (which is converted to a string), representing the javascript source code of the module. As additional optional second result is a SourceMap (as JSON object) expected.
A single result can be returned in sync mode. For multiple result the this.callback
must be called. In async mode this.async()
must be called. It returns this.callback
if async mode is allowed. Then the loader must return undefined
and call the callback.
Errors can be thrown in sync mode or the this.callback
can be called with the error.
webpack
allows async mode in every case.
enhanced-require
allows async mode only with require.ensure
or AMD require
.
module.exports = function(content) {
return someSyncOperation(content);
};
module.exports = function(content) {
var callback = this.async();
if(!callback) return someSyncOperation(content);
someAsyncOperation(content, function(err, result) {
if(err) return callback(err);
callback(null, result);
});
};
Note: It's recommended to give an asnychron loader and fallback sync mode. This isn't required for webpack, but allows to run the loader sync in enhanced-require.
By default the resource file is threaded as utf-8
string and passed as String to the loader. By setting raw
to true
the loader is passed the raw Buffer.
Every loader is allowed to deliver its result as String or as Buffer. The compiler converts them between loaders.
module.exports = function(content) {
assert(content instanceof Buffer);
return someSyncOperation(content);
// return value can be a Buffer too
// This is also allowed if loader is not "raw"
};
module.exports.raw = true;
The loaders are called from right to left. But in some cases loaders doesn't care for the results of the previous loader or the resource. They only care for metadata. The pitch
method on the loaders is called from left to right before the loaders are called. If a loader delivers a result in the pitch method the process turns around and skips the remaining loaders, continuing with the calls to the more left loaders. data
can be passed between pitch and normal call.
module.exports = function(content) {
return someSyncOperation(content, this.data.value);
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {
if(someCondition()) {
// fast exit
return "module.exports = require(" + JSON.stringify("-!" + remainingRequest) + ");";
}
data.value = 42;
};
This stuff is available on this
in a loader.
For the example this require call is used:
In /abc/file.js
:
require("./loader1?xyz!loader2!./resource?rrr");
Loader API version. Currently 1
.
A string. The directory of the module. Can be used as context for resolving other stuff.
In the example: /abc
because resource.js
is in this directory
The resolved request string.
In the example: "/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr"
A string. The query of the request for the current loader.
In the example: in loader1: "?xyz"
, in loader2: ""
A data object shared between the pitch and the normal phase.
cacheable(flag = true: boolean)
Make this loader result cacheable. By default it's not cacheable.
A cacheable loader must have a deterministic result, when inputs and dependencies havn't changed. This means the loader shoudn't have other dependencies than specified with this.addDependency
. Most loaders are deterministic and cachable.
loaders = [{request: string, path: string, query: string, module: function}]
An array of all the loaders. It is writeable in the pitch phase.
In the example:
[
{ request: "/abc/loader1.js?xyz",
path: "/abc/loader1.js",
query: "?xyz",
module: [Function]
},
{ request: "/abc/node_modules/loader2/index.js",
path: "/abc/node_modules/loader2/index.js",
query: "",
module: [Function]
}
]
The index in the loaders array of the current loader.
In the example: in loader1: 0
, in loader2: 1
The resource part of the request, including query.
In the example: "/abc/resource.js?rrr"
The resource file.
In the example: "/abc/resource.js"
The query of the resource.
In the example: "?rrr"
emitWarning(message: string)
Emit a warning.
emitError(message: string)
Emit an error.
exec(code: string, filename: string)
Execute some code fragment like a module.
Hint: Don't use
require(this.resourcePath)
, use this function to make loaders chainable!
resolve(context: string, request: string, callback: function(err, result: string))
Resolve a request like a require expression.
resolveSync(context: string, request: string) -> string
Resolve a request like a require expression.
addDependency(file: string)
dependency(file: string) // shortcut
Add a file as dependency of the loader result.
addContextDependency(directory: string)
Add a directory as dependency of the loader result.
clearDependencies()
Remove all dependencies of the loader result. Even initial dependencies and these of other loaders. Consider using pitch
.
Pass values to the next loaders inputValues
. If you know what your result exports if executed as module, set this value here (as a only element array).
Passed from the last loader. If you would execute the input argument as module, consider reading this variable for a shortcut (for performance).
The options passed to the Compiler.
A boolean flag. It is set when in debug mode.
Should the result be minimized.
Target of compilation. Passed from configuration options.
Example values: "web"
, "node"
Set to true when this is compiled by webpack.
emitFile(name: string, content: Buffer|String, sourceMap: {...})
Emit a file. This is webpack-spcific
Hacky access to the Compilation object of webpack.
Hacky access to the Compiler object of webpack.
webpack 👍