Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack loaders order: what are webpack pre and post loaders and how they differ from a chain of loaders

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:

  • what is the difference between chaining and pre and post ?
  • is there a way to have a more verbose webpack log on the sequence of this chain to understand what runs first and what second?

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

like image 385
stilllife Avatar asked Jan 19 '18 10:01

stilllife


People also ask

What are webpack loaders?

Loaders are transformations that are applied to the source code of a module. They allow you to pre-process files as you import or “load” them. Thus, loaders are kind of like “tasks” in other build tools and provide a powerful way to handle front-end build steps.

What are 4 core concept of webpack?

There are four basic concepts in webpack: entry , output , modules and plug-ins . These configurations are added in webpack.

What's the difference between webpack loaders and plugins?

Loaders work at the individual file level during or before the bundle is generated. Plugins: Plugins work at bundle or chunk level and usually work at the end of the bundle generation process. Plugins can also modify how the bundles themselves are created.

What is webpack and how does it work?

When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.


1 Answers

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.

like image 58
Jordan Nelson Avatar answered Sep 22 '22 18:09

Jordan Nelson