Every webpack resolve plugin follows the following structure:
resolver.plugin(this.source, function(request, callback) {
if (something) {
resolver.doResolve(target, obj, "aliased with mapping '" + name + "': '" + ...)
} else {
callback(...);
}
Can anyone please explain when I should call doResolve
and when callback. I've found this phrase in the webpack docs:
To pass the request to other resolving plugins, use the this.doResolve(types: String|String[], request: Request, callback) method
However, I don't know what to make of it. It seems that doResolve
starts the process from the start. Here is how the stack looks like in doResolve
:
You can see that the stages started from the beginning. Why?
Webpack runs all plugins for the resolver using applyPluginsAsyncSeriesBailResult1
method. This method runs all plugins in the succession - the next plugin is run only after the current plugin has finished executing.
The bail
here means that the sequence is interrupted as soon as one plugin returns an error. This approach is also known as "fail-fast". But BailResult
means that the sequence is also interrupted as soon as one plugin returns a result. You can see it from the sources:
applyPluginsAsyncSeriesBailResult1 = function applyPluginsAsyncSeriesBailResult1(name, param, callback) {
var plugins = this._plugins[name];
if(!plugins || plugins.length === 0) return callback();
var i = 0;
var _this = this;
var innerCallback = function next(err, result) {
// if the plugin returned an error or a result - break
if(arguments.length > 0) return callback(err, result);
i++;
// if all plugins have run - break
if(i >= plugins.length) {
return callback();
}
// trigger next plugin - continue
plugins[i].call(_this, param, innerCallback);
});
plugins[0].call(this, param, innerCallback);
};
So from this code you can see that as soon as you call callback
inside the plugin with parameters you interrupt the sequence. The first parameter is the error, the second parameter is the result. This is in line with how Node.js
expects the callbacks signature. If you call the callback
without parameters, the sequence is continued.
Now, you can also call doResolve
which will run the entire sequences of plugins again. This is usually done when you apply some changes to the request and so you want to give a chance to all other plugins again to react to the new request. As your current plugin will be called again on the next doResolve
round make sure to construct it so that it prevents recursion. Webpack guards again recursion but only if path
, request
, query
directory
and module
match as can be seen from the sources:
Resolver.prototype.doResolve = function doResolve(type, request, message, callback) {
var resolver = this;
var stackLine = type + ": (" + request.path + ") " +
(request.request || "") + (request.query || "") +
(request.directory ? " directory" : "") +
(request.module ? " module" : "");
var newStack = [stackLine];
if(callback.stack) {
newStack = callback.stack.concat(newStack);
if(callback.stack.indexOf(stackLine) >= 0) {
// Prevent recursion
var recursionError = new Error("Recursion in resolving\nStack:\n " + newStack.join("\n "));
And inside the callback to the doResolve
you usually call callback()
to break from the current round of plugins since they have had their chance to react to the updated request:
resolver.plugin(this.source, function(request, callback) {
if (something) {
resolver.doResolve(target, obj,
"aliased with mapping '" + name + "': '" + ...,
function() { callback(error, result) }
)