I get that in the newest Webpack we can specify the module.rules
option enforce: 'pre'
to make a certain loader run as a "pre-loader" as specified in the docs.
But I couldn't find any proper explanation of what pre-loader and post-loader means. Off course we can logically think that "pre" runs before "post" but I don't get what EXACTLY happens (and why is not documented?).
This is also considering that there is already a way to specify the loaders order looking at the property Rule.use
in the docs which says Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured)
So two connected questions:
PS 1: I know there are similar questions on SO but none that I found is linking to a piece of documentation that actually explains the loading order in details
PS 2: a brief scenario on why this seems important to me is that I run typescript, tslint and babel and I would like to understand the correct chaining process and what is actually going on in the various steps
To discover the answer I wrote my own loaders a-loader.js
through h-loader.js
that take in content, print a log, and then return the content. Each loader file has a normal phase and a pitching phase for completeness. You can read about pitching loaders here https://webpack.js.org/api/loaders/#pitching-loader.
a-loader.js
:
module.exports = function(content) {
console.log('Loader A, normal phase.');
return content;
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {
console.log('Loader A, pitching phase.');
}
All the loaders have identical code except I changed the logging statement to log which loader it is.
My webpack-config.js
looked like this:
module: {
rules: [
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/a-loader.js')}],
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/b-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/c-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/d-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/e-loader.js')}],
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/f-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/g-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/h-loader.js')}]
},
]
}
Output:
Loader A, pitching phase.
Loader B, pitching phase.
Loader C, pitching phase.
Loader D, pitching phase.
Loader E, pitching phase.
Loader F, pitching phase.
Loader G, pitching phase.
Loader H, pitching phase.
Loader H, normal phase.
Loader G, normal phase.
Loader F, normal phase.
Loader E, normal phase.
Loader D, normal phase.
Loader C, normal phase.
Loader B, normal phase.
Loader A, normal phase.
No surprise here. The pitching phases run first, and then the normal phases run. As you pointed out, normal phase loaders are applied right-to-left. h
is first in the normal phase because it is the furthest right in the array (chain). I have a helpful way to remember the pitching order. Just think about the normal phase order and imagine a mirror image projected above the normal order. The mirror image is the pitching order.
Next, I adjusted the webpack.config.js
to be the following:
module: {
rules: [
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/a-loader.js')}],
enforce: "pre"
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/b-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/c-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/d-loader.js')}],
enforce: "post"
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/e-loader.js')}],
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/f-loader.js')}],
enforce: "post"
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/g-loader.js')}]
},
{
test: /\.js$/,
use: [{loader: path.resolve('loaders/h-loader.js')}],
enforce: "pre"
},
]
}
Ouput:
Loader D, pitching phase.
Loader F, pitching phase.
Loader B, pitching phase.
Loader C, pitching phase.
Loader E, pitching phase.
Loader G, pitching phase.
Loader A, pitching phase.
Loader H, pitching phase.
Loader H, normal phase.
Loader A, normal phase.
Loader G, normal phase.
Loader E, normal phase.
Loader C, normal phase.
Loader B, normal phase.
Loader F, normal phase.
Loader D, normal phase.
Ignore the pitching phase for a moment, because remember they are just a mirror reflection of the normal phase. Think of enforce: pre
and post
like groupings. The "pre" are the first group, then comes the unlabeled "normal" group, and finally the "post" group. In normal phase, the first loader is h
because it is in the "pre" group and is furthest right in the array. Next is a
because it is the only other one in the "pre" group. Next comes the "ungrouped" g
, e
, c
, b
from right-to-left. And finally the "post" group, f
and d
, runs in right-to-left order.
I don't know why this isn't documented better on the webpack site.