Search code examples
javascriptwebpackbabel-loader

Can I use a webpack hook to modify file output just before it gets saved?


I want to manipulate a file after it has been processed by webpack and babel. There's an emit hook that is triggered just before a new file is saved, but I couldn't see a way to manipulate the file contents. So I settled for using the afterEmit hook to read in the just-written file, modify it, and write it back out:

    plugins: [
      new class OutputMonitor {
        apply(compiler) {
          compiler.hooks.afterEmit.tap('OutputMonitor', compilation => {
            if (compilation.emittedAssets.has('index.js')) {
              let contents = fs.readFileSync('./dist/web/index.js', 'utf-8');
              // Strip out dynamic import() so it doesn't generate warnings.
              contents = contents.replace(/import(?=\("tseuqer-yb")/, 'console.log');
              // Strip out large and large-alt timezone definitions from this build.
              contents = contents.replace(large, 'null');
              contents = contents.replace(largeAlt, 'null');
              fs.writeFileSync('./dist/web/index.js', contents);
            }
          });
        }
      }()
    ],

This gets the job done, but is there a better way?


Solution

  • From what I can tell, you're basically replacing some strings with another strings.

    I believe you can use processAssets hook if you're running webpack 5.

    Here's an example you can adapt to your case:

    const { Compilation, sources } = require('webpack');
    
    class Replace {
      apply(compiler) {
        compiler.hooks.thisCompilation.tap('Replace', (compilation) => {
          compilation.hooks.processAssets.tap(
            {
              name: 'Replace',
              stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
            },
            () => {
              // get the file main.js
              const file = compilation.getAsset('main.js');
              // update main.js with new content
              compilation.updateAsset(
                'main.js',
                new sources.RawSource(file.source.source().replace('a', 'b'))
              );
            }
          );
        });
      }
    }
    module.exports = {
      entry: './wp.js',
      plugins: [new Replace()],
    };