9

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?

Dharman
  • 30,962
  • 25
  • 85
  • 135
kshetline
  • 12,547
  • 4
  • 37
  • 73
  • I didn't want to make it look like I was actually providing the answer to the question, I wanted to show what chenxsan solution looked like, as I adapted it, without looking like I was taking credit for it. – kshetline Jan 02 '21 at 01:10
  • If you think that your adaptation will help other people then you should post it as an alternative answer. If it's not going to add much to the topic then leave it as it is now. – Dharman Jan 02 '21 at 01:35

1 Answers1

9

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()],
};

Sam Chen
  • 1,933
  • 16
  • 24
  • This *almost* looks like it would work for me, and it's definitely helpful info, because I had no idea that there were hooks inside hooks like this for the compilation process. The snag is that my editing was based on modifying minified code, but the above plugin is intercepting the output before it has been minified. I guess I'd need some sort of hook into the TerserPlugin? – kshetline Jan 01 '21 at 19:06
  • I kept thinking about how much nicer this solution of yours is, and realized I could use a different (and better) approach to finding the code I wanted to delete -- marker comments. That's actually much better than the exact character matches for the large blocks of data that I was using. I'll append my solution to my question. – kshetline Jan 02 '21 at 00:46
  • Since someone edited away my updated code, here's the key point: `contents.replace(/\/\* trim-file-start \*\/.*?\/\* trim-file-end \*\//sg, 'null');`, a regex that looks for special start and end comments around the code to be replaced. – kshetline Jan 02 '21 at 01:17
  • There're many stages to use for `processAssets` hook. For example, TerserPlugin use `PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE` https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/src/index.js#L628, which means you might use a hook after that stage if you want to modify minified code. But I haven't tested it. – Sam Chen Jan 02 '21 at 02:48
  • Worked great for me. Just wanted to add a line of CSS to my styles.css, this did the trick! – Chris Hayes Feb 09 '22 at 20:11
  • if you need optimized assets, after the TerserPlugin, you should use ```afterOptimizeAssets``` instead of ```processAssets``` – fela Jan 13 '23 at 10:30