Home > Enterprise >  How to inspect files compiled by webpack before they are emitted
How to inspect files compiled by webpack before they are emitted

Time:05-17

I'm using webpack (v4) to package assets for my UI application. I'd like to run a function that checks the contents of the compiled assets for invalid strings ideally before the files are emitted to the output directory. In coming up with an initial approach, I happily stumbled upon webpack hooks which seems to be exactly what I need to accomplish my task.

Unfortunately, the documentation is a little lacking or at least I'm still left with some uncertainty after reviewing it. My first challenge is determining which hook to use. It seems like I need a Compiler hook (rather than Compilation hook) and the emit hook seems to be the one I'm interested in. Going with that, here is what I have so far:

plugins: [
  {
    apply(compiler: webpack.Compiler) {
      compiler.hooks.emit.tap('ValidateEmittedAssetsPlugin', (compilation) => {
        const { assets } = compilation;

        Object.entries(assets).forEach(([pathname, source]) => {
          const contents = (source as Buffer).buffer.toString();
          if (contents.indexOf(SOME_INVALID_STRING) > -1) {
            console.log('File contained invalid string. Failing build.');
            return false;
          }

          // Trying to toString() source.buffer hasn't worked so I've also tried 
          // the following also to no avail.
          //
          // const file = compilation.getAsset(pathname);
          // const contents = file.source.buffer.toString();
          }
        });
        return true;
      });
    },
  },
]

I am able to successfully iterate through the files to be emitted, but I am unable to get the contents of those assets. Since they are (I'm guessing) in-memory, I can't use something like fs to read the files from the file system. I also attempted to use compilation.getAsset() (as noted above) to no avail. So my challenge is getting the contents of the in-memory assets in the emit hook.

How can I use webpack hooks to check the contents of a compiled asset before it is emitted?

CodePudding user response:

If you want control the emitting assets you must use compiler.hooks.shouldEmit hook, with it's return value you can actually control. Which you cant control with compiler.hooks.emit hook. Both execute before emitting assets but shouldEmit allow to control emitting assets.

Once you get entries from compilation.asset, you can call compilation.getAsset(<asset_name>) to get asset and asset.source.source() returns you the content.

You can use either emit or shouldEmit hook depending on your preference.

I prefer having separate js file to manage output, rather plugin logic into module.plugin array. You can also pass configuration into constructor to have more control over output.

AssetValidatorPlugin.js

const PLUGIN_NAME = 'AssetValidatorPlugin';

class AssetValidatorPlugin {

    constructor(options){
        this.options = options;
    }

    apply(compiler) {
        // to check content of emitted assets
        compiler.hooks.emit.tap(PLUGIN_NAME, (compilation) => {
            let assets = Object.entries(compilation.assets);
            assets.forEach(([fileName]) => {
                const asset = compilation.getAsset(fileName);
                const assetContent = asset.source.source();
                console.log(assetContent);
            })
        });

        // to check content of emitted assets and prevent when fail to pass checks
        compiler.hooks.shouldEmit.tap(PLUGIN_NAME, (compilation) => {
            let allCheckPassed = true;
            let assets = Object.entries(compilation.assets);
            const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);

            logger.info('Asset check');

            for (let fileIndex = 0; fileIndex < assets.length; fileIndex  ) {
                const [fileName] = assets[fileIndex];

                logger.info(`validating ${fileName}`)
                const asset = compilation.getAsset(fileName);
                const assetContent = asset.source.source();

                if (assetContent.indexOf('some inane string') > -1) {
                    logger.error(`invalid string found in ${fileName} `);
                    allCheckPassed = false;
                    break;
                }
            };

            logger.info('Asset check completed');
            if (allCheckPassed)
                logger.info('asset emit proceeded');
            else
                logger.warn('asset emit aborted');
            
            if(this.options.stopEmitOnError)
                return allCheckPassed;

            return true;
        });
    }
}

module.exports = AssetValidatorPlugin;

and add it to webpack.config.js

const AssetValidatorPlugin = require('./AssetValidatorPlugin');

module.exports= {
    entry: {...},
    module:{...},
    plugins: [
        new AssetValidatorPlugin({stopEmitOnError: true}),
        ...
    ]
};

Have tested on Webpack v4/v5 both hooks works the same. Also you can get the asset content in same manner.

  • Related