Do you know why the Loader is executed from right to left?

Before we get into this, let’s understand a little bit about the basics of loader

No matter what module is processed by the loader, it will become a JS module

The leftmost loader return value must be a JS module, otherwise webpack won’t recognize modules in other languages (webpack only knows JS and JSON)

1. Type of Loader

The loader is divided into four categories: the loaders are stacked in the order of post (post) + inline (inline) + normal (nomore) + pre (pre)

How to specify the type of loader

Specify the type by enforce: ‘post’/‘pre’, if not it is nomore
inline-loade needs to be written in-line

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
rules: [
{
test: /.js/,
use: [
path.resolve(__dirname, 'loaders', 'nomore-loader.js')
]
},

{
test: /.js/,
enforce: 'pre',
use: [
path.resolve(__dirname, 'loaders', 'pre-loader.js')
]
},

{
test: /.js/,
enforce: 'post',
use: [
path.resolve(__dirname, 'loaders', 'post-loader.js')
]
}
]

const title = require('inline-loader!./title');

2. The execution order of Loader

4 post loader <= 3 inline loader <= 2 nomore loader <= 1 pre loader

will execute pre loader, nomore loade, inline loader, post loader first

This order is what we call right-to-left execution

2.1 pitch

What is pitch?

Every loader has two functions, one is nomore and one is pitch

pitch is an attribute of the loader, it is also a function, and the execution order of pitch is from left to right, you can see the following figure

If the function of pitch has a return value, it will default to the back of the loader are executed, it will execute the loader function of the last loader, and the return value will be used as a parameter of the last loader function

If there is no return value, it will execute the next inline-loader’s pitch method, and if there is no return value, it will continue to execute until the file is read, then it will execute the nomore, which is the above loader function, from back to front. If the pitch has a return value, it is assumed by default that all the pitches and nomore are executed, and then the nomore function of the previous one is executed.

3. special configuration of loader

3.1! noAutoLoaders

No normal loaders aka nomore

3.2 -! noPreAutoLoaders

No pre-loaders and no normal loaders, i.e. pre-loaders and nomore-loaders

3.3 !!! noPrePostAutoLoaders

No pre-post normal loaders, just in-line loaders, i.e. just inline-loaders

4. Loader-Runner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const { runLoaders } = require('loader-runner');
const path = require('path');
const fs = require('fs');

const enrtyFile = path.resolve(__dirname, 'src', 'title.js');
const rules = [
{
test: /title.js$/,
use: ["nomore-loader.js"]
},
{
test: /title.js$/,
enforce: 'post',
use: ["post-loader.js"]
},
{
test: /title.js$/,
enforce: 'pre',
use: ["pre-loader.js"]
}
];


let request = `!!inline-loader!${enrtyFile}`;
let parts = request.replace(/^-?!+/, '').split('!'); // [inline-loader, enrtyFile];
let resource = parts.pop();
const inlineLoaders = parts;

const preLoaders = [],
postLoaders = [],
nomoreLoaders = [];

rules.forEach(rule => {
if (rule.test.test(resource)) {
if (rule.enforce === 'pre') {
preLoaders.push(...rule.use)
} else if (rule.enforce === 'post') {
postLoaders.push(...rule.use)
} else {
nomoreLoaders.push(...rule.use)
}
}
});

let loaders = [];
if (request.startsWith('!!')) {
loaders = [...inlineLoaders];
} else if (request.startsWith('!')) {
loaders = [...postLoaders, ...inlineLoaders, ...preLoaders];
} else if (request.startsWith('-!')) {
loaders = [...postLoaders, ...inlineLoaders];
} else {
loaders = [...postLoaders, ...inlineLoaders, ...nomoreLoaders, ...preLoaders];
};

const resolveLoader = loader => path.resolve(__dirname, 'loader', loader);
loaders = loaders.map(resolveLoader);

runLoaders({
resource,
loaders,
context: {name: 'test loader'},
readResource: fs.readFile.bind(fs)
}, (err, result) => {
console.log('err', err);
console.log('result', result);
console.log('buffer', result.resourceBuffer.toString('utf8'));
})

img