afterwind-io / preprocessor-loader

Bring the awesome "Conditional Compilation" to the Webpack, and more.
MIT License
40 stars 12 forks source link

Doesn't work with cache in webpack 5 #16

Open Tofandel opened 3 years ago

Tofandel commented 3 years ago

When cache is enabled with webpack 5, modifying the preprocessor options will not trigger a cache bust and thus will use the cached version of the last compiled file as if the options were not changed

afterwind-io commented 3 years ago

This might help maybe? https://webpack.js.org/configuration/other-options/#cachebuilddependencies

It's recommended to set cache.buildDependencies.config: [__filename] in your webpack configuration to get the latest configuration and all dependencies.

BTW, I built a minimal example and found it actually works. Did i miss something?

./src/index.js

window.a = 1;

// #!debug
export const a = 1;

// #!if a === 1
export const b = 2;
// #!endif

webpack.config.js

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  cache: true,
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'webpack-preprocessor-loader',
            options: {
              params: { a: 2 },
              debug: false,
            },
          },
        ],
      },
    ],
  },
};

package.json

"dependencies": {
    "webpack": "^5.36.0",
    "webpack-cli": "^4.6.0",
    "webpack-preprocessor-loader": "^1.1.4"
}

Change the config value params: { a: 2 } to params: { a: 1 } does update the packed result in ./dist/main.js based upon the setup above.

Tofandel commented 3 years ago

Modifying webpack.config.js invalidates the cache (as it is itself a build deps) try using an env variable and the filesystem

Try this

console.log(process.env.TEST);
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  cache:  {
    type: 'filesystem',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'webpack-preprocessor-loader',
            options: {
              params: { a: parseInt(process.env.TEST) },
              debug: false,
            },
          },
        ],
      },
    ],
  },
};

Then run

TEST=1 webpack
TEST=2 webpack

Output will still be the one of 1

Tofandel commented 3 years ago

It seems it's possible to use something of the like to get the correct behavior

 cache:  {
    type: 'filesystem',
    version: process.env.TEST // Should create a hash of the env variables that modify the build
  },
const hash = require('object-hash');
const options =  {
  params: { a: parseInt(process.env.TEST) },
  debug: false,
};
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  cache:  {
    type: 'filesystem',
    version: hash(options),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'webpack-preprocessor-loader',
            options,
          },
        ],
      },
    ],
  },
};

It doesn't seem to be possible to use variables directly into the loader cache deps to calculate the cache only files, this seems like a good feature request for webpack

https://webpack.js.org/api/loaders/#thisadddependency

https://github.com/webpack/webpack/issues/13249

Tofandel commented 3 years ago

It seems it possible to hack this somehow! https://github.com/webpack/webpack/blob/00f4a82903d99502ffa1eee5c0bfd2fa405d04b8/lib/NormalModule.js#L842

Just need to add the options to this buildMeta variable :( This doesn't rerun on rebuild if the file is already cached, so dead end

afterwind-io commented 3 years ago

I see the pain here...

JUST FOR FUN, try this perhaps?

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    // Write your env param to a temp file
    "build1": "echo 1>foo&& TEST=1 webpack",
    "build2": "echo 2>foo&& TEST=2 webpack"
},

webpack.config.js

cache: {
    type: 'filesystem',
    buildDependencies: {
      // Add the `foo` to the list. You get the dependency changes, and reuse the cache to its best.
      // Modify the `version` may break the actual cache control strategy.
      config: [__filename, 'foo'],
    },
},
Tofandel commented 3 years ago

That works with config: [__filename, './foo'], but it's so hackish :face_with_head_bandage: I'll keep the hash-object-version workaround in the meantime

It's technically possible to write a file inside the package and add this as dep of the loader until the feature comes along, except this would have the same problem, the loader function doesn't run again if it's cached... So a bit of a dead end (It's possible to hook into webpack when the loader is imported, but that always ends up in a lot of ugly hacks). This needs to be fixed upstream.