Daniel Wolf
Daniel Wolf

Reputation: 13693

Webpack loaders: Are options considered when caching?

I'm writing a Webpack loader that should be cacheable. The documentation for this.cacheable says:

A cacheable loader must have a deterministic result, when inputs and dependencies haven't changed.

Now I wonder: Do the loader's options count as "input" in this sense?

Assume I have a Webpack build that uses a loader with options { foo: 1 } on a module. In the next compilation, it uses the same loader on the same module, but with options { foo: 2 }. Will the output from the first compilation be re-used, or will Webpack (correctly) realize that the loader's options have changed and thus re-load the module?

Assuming that Webpack does compare the options objects, how are they compared? By reference? Using a deep comparison? The comparison logic would dictate what types of data I can safely use as loader options.

Upvotes: 0

Views: 509

Answers (2)

Keith Rousseau
Keith Rousseau

Reputation: 4485

If you change the loader's inputs then you're essentially creating a new instance of it so it should not use the cache from the loader with different options.

If you look here, it looks like it's just setting a flag that says whether to request cacheable or not and it's up to you to cache results. It's always going to call your loader no matter what. https://github.com/webpack/loader-runner/blob/master/lib/LoaderRunner.js#L270

Upvotes: 0

Daniel Wolf
Daniel Wolf

Reputation: 13693

Short version:

Webpack identifies a module via its request string. This string contains the file path as well as the paths and options of all involved loaders. This effectively means that changing the options on one loader will result in a different request string, so the previous load result will not be re-used.

Long version:

Request strings are "!"-separated and are created by this code.

Consider this request string:

"/Users/wolf/dev/webpack-test/test-loader.js!/Users/wolf/dev/webpack-test/patch-loader.js??ref--0-0!/Users/wolf/dev/webpack-test/src/foo.js"

This means (from right to left) that file /Users/wolf/dev/webpack-test/src/foo.js is to be loaded using first the loader /Users/wolf/dev/webpack-test/patch-loader.js with options ref--0-0, then the loader /Users/wolf/dev/webpack-test/test-loader.js without any options.

The relevant question, now, is how Webpack serializes the loaders' options when creating the request string. The answer can be found in this code:

Let loaderData be an object of this form:

{
  loader: string, // Path to loader
  options: string | object | null | undefined,
  ident: string | null | undefined
}
  • If loaderData.options is a string, it is used as-is.
  • Otherwise, if loaderData.ident is truthy, this value is used.
  • Otherwise, loaderData.options is stringified using JSON.stringify.

It appears that when loaders are specified statically, Webpack knows that their options won't change. So RuleSet.normalizeRule() assigns them fixed ident properties. This is what happened in the example above with ref--0-0.

One key insight here is that loader options should be rather simple objects so that calling JSON.stringify on them doesn't lose any information. Otherwise, Webpack may inadvertently re-use a cached result that used different loader options. Alternatively, you can explicitly set loaderData.ident to a string that uniquely identifies the options (such as a hash code).

Upvotes: 1

Related Questions