Webpack plugin to modify files after compilation
Asked Answered
A

2

11

I am writing a Webpack plugin that should replace a part of the JS code with a list of Webpack's generated CSS files. Imaging this JS code:

ReactWebComponent.create(<App />, 'react-web-component', { injectReactWebComponent: true });

I want to replace the injectReactWebComponent: true part with

injectReactWebComponent: '<link href="static/css/main.cacbacc7.css" rel="stylesheet">'

While that link tag is a file generated by Webpack.

My (kind of working) code for this goes as follows:

ReactWebComponent.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    const cssFiles = Object.keys(compilation.assets)
        .filter(fileName => /\.css$/.test(fileName));

    const jsFiles = Object.keys(compilation.assets)
        .filter(fileName => /\.js$/.test(fileName));

    const cssLinkTagsAsString = cssFiles
        .map(fileName => `<link href="${fileName}" rel="stylesheet">`)
        .join('');

    jsFiles.forEach(fileName => {
      compilation.assets[fileName].children.forEach(child => {
        if (child._value) {
          child._value = child._value.replace(/injectReactWebComponent\s*:\s*true/g, `injectReactWebComponent: \'${cssLinkTagsAsString}\'`);
        }
      });
    });

    callback();
  });
};

module.exports = ReactWebComponent;

See the line where I do

child._value = child._value.replace(...)

I am bluntly rewriting the sources.

But this does not seem right for me. It seems the code I am transforming has already been generated and not ought to be transformed anymore. I am also pretty sure I am breaking the source maps with this.

So I am wondering, what would be an appropriate way to achieve what I am doing?

I am guessing for the transformation part I should use a loader, but then loaders do not know about the generated file names, or do they?

Any help would be appreciated!

Astrionics answered 11/8, 2017 at 22:25 Comment(0)
C
8

It appears this sort of thing is simple enough to be handled by loaders, of which there are multiple (some better than others):

https://www.npmjs.com/package/string-replace-loader

https://www.npmjs.com/package/regexp-replace-loader

https://www.npmjs.com/package/replace-loader

If you want to go about making your own plugin to do this (I just went through the process to do something a bit more sophisticated than just regex replacement), I was able to piece my solution together from basically these three pages:

https://github.com/webpack/webpack/blob/master/lib/BannerPlugin.js

https://webpack.js.org/api/plugins/compilation/#optimize-chunk-assets-chunks-chunk-async

https://github.com/webpack/webpack-sources

Cuprite answered 15/9, 2017 at 1:28 Comment(2)
It looks like loaders do their transformation before webpack. Is there a way to do that after?Mose
@Mose yes there is, check out github.com/artembatura/modify-source-webpack-pluginEmpiricism
E
0

I know this question is old, but I needed to do something similar (I needed to modify Quasar 1.x's client-entry.js file so I could turn a quasar app into a callable library from someone else's JavaScript, i.e. I had to stop the app from loading automatically, and there is no Quasar setting for this, however, all I needed to do was replace start() in .quasar/client-entry.js with my wrapper function).

Since both myself and the author just need to modify webpack files after they are compiled, and this page is a promonent result for people searching for how to do something similar, and it didn't matter that I was using Vue and they are using React, I'm posting how I solved it here for the author's possible benefit and others.

Note: Quasar 1.x uses webpack 4, and Quasr 2.x uses webpack 5. I tried this plugin on both versions of webpack (4 & 5) and it works beautifully!

https://github.com/artembatura/modify-source-webpack-plugin

This plugin's readme.md file contains everything you need to use it. It even comes with concat and replace helper functions!

Empiricism answered 24/7, 2023 at 13:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.