19

I'm using Webpack's [hash] for cache busting locale files. But I also need to hard-code the locale file path to load it from browser. Since the file path is altered with [hash], I need to inject this value to get right path.

I don't know how can get Webpack [hash] value programmatically in config so I can inject it using WebpackDefinePlugin.

module.exports = (env) => {
  return {
   entry: 'app/main.js',
   output: {
      filename: '[name].[hash].js'
   }
   ...
   plugins: [
      new webpack.DefinePlugin({
         HASH: ***???***
      })
   ]
  }
}
  • 1
    You can get hash and chunk name as global variables in runtime in Webpack 4 with the help of [ExtendedAPIPlugin](https://v4.webpack.js.org/plugins/extended-api-plugin/#usage) Or [APIPlugin](https://github.com/webpack/webpack/blob/master/lib/APIPlugin.js) in Webpack v5 (take a look at the topic about migration from v4 to v5 for more detail: https://webpack.js.org/migrate/5/#upgrade-all-used-plugins-and-loaders-to-the-latest-available-version) – SenDen Aug 04 '20 at 19:06

7 Answers7

17

In case you want to dump the hash to a file and load it in your server's code, you can define the following plugin in your webpack.config.js:

const fs = require('fs');

class MetaInfoPlugin {
  constructor(options) {
    this.options = { filename: 'meta.json', ...options };
  }

  apply(compiler) {
    compiler.hooks.done.tap(this.constructor.name, stats => {
      const metaInfo = {
        // add any other information if necessary
        hash: stats.hash
      };
      const json = JSON.stringify(metaInfo);
      return new Promise((resolve, reject) => {
        fs.writeFile(this.options.filename, json, 'utf8', error => {
          if (error) {
            reject(error);
            return;
          }
          resolve();
        });
      });
    });
  }
}

module.exports = {
  // ... your webpack config ...

  plugins: [
    // ... other plugins ...

    new MetaInfoPlugin({ filename: 'dist/meta.json' }),
  ]
};

Example content of the output meta.json file:

{"hash":"64347f3b32969e10d80c"}

I've just created a dumpmeta-webpack-plugin package for this plugin. So you might use it instead:

const { DumpMetaPlugin } = require('dumpmeta-webpack-plugin');

module.exports = {
  ...

  plugins: [
    ...

    new DumpMetaPlugin({
      filename: 'dist/meta.json',
      prepare: stats => ({
        // add any other information you need to dump
        hash: stats.hash,
      })
    }),
  ]
}

Please refer to the Webpack documentation for all available properties of the Stats object.

Dmitry Druganov
  • 2,088
  • 3
  • 23
  • 32
  • 1
    Druganor, thanks fo this, I ended up writing out metadata to a file just like so. I needed chunk.name -> chunk.renderedHash map. stats.compilation.chunks.forEach( chunk => { metaInfoObject[chunk.name] = chunk.renderedHash }) – Elijah Dec 12 '19 at 16:53
2

Seems like it should be a basic feature but apparently it's not that simple to do.

You can accomplish what you want by using wrapper-webpack-plugin.

plugins: [
  new WrapperPlugin({
    header: '(function (BUILD_HASH) {',
    footer: function (fileName) {
      const rx = /^.+?\.([a-z0-9]+)\.js$/;
      const hash = fileName.match(rx)[1];
      return `})('${hash}');`;
    },
  })
]

A bit hacky but it works — if u don't mind the entire chunk being wrapped in an anonymous function. Alternatively you can just add var BUILD_HASH = ... in the header option, though it could cause problem if it becomes a global.

I created this plugin a while back, I'll try to update it so it provides the chunk hash naturally.

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Acidic
  • 6,154
  • 12
  • 46
  • 80
  • Thanks for your answer. I achieved it using ExtendedAPIPlugin which provided me a global variable __webpack_hash__. –  May 08 '18 at 14:45
2

On server, you can get the hash by reading the filenames (example: web.bundle.f4771c44ee57573fabde.js) from your bundle folder.

ZachB
  • 13,051
  • 4
  • 61
  • 89
VnDevil
  • 1,321
  • 15
  • 13
  • 1
    Yeah, +1, at the end of the day this is a really pragmatic, easy to understand solution that doesn't involve any magic or obscure webpack config. Going with this. – davnicwil Feb 14 '20 at 13:26
2

The WebpackManifestPlugin is officially recommended in the output management guide. It writes a JSON to the output directory mapping the input filenames to the output filenames. Then you can inject those mapped values into your server template.

It's similar to Dmitry's answer, except Dmitry's doesn't appear to support multiple chunks.

ZachB
  • 13,051
  • 4
  • 61
  • 89
0

You can pass the version to your build using webpack.DefinePlugin

If you have a package.json with a version, you can extract it like this:

const version = require("./package.json").version;

For example (we stringified the version):

new webpack.DefinePlugin({
    'process.env.VERSION': JSON.stringify(version)
}),

then in your javascript, the version will be available as:

process.env.VERSION
Wouter Coebergh
  • 804
  • 1
  • 6
  • 20
  • I'm getting `version` is undefined while building. Where is that `version` defined? –  May 08 '18 at 07:43
  • We never bump version in package.json. We've different method to do versioning. I can't inject hash directly? –  May 08 '18 at 07:54
  • Ah like that, sorry I did not notice that. Maybe this answer will help? https://stackoverflow.com/a/39085075/4496465 – Wouter Coebergh May 08 '18 at 08:19
  • Great. ExtendedAPIPlugin provided __webpack_hash__ as global variable. Thanks! But I'm wondering how many such variables are injected into my code. –  May 08 '18 at 14:27
  • Just looked at the source, looks like it is only 2; __webpack_hash__ and __webpack_chunkname__ . Source: https://github.com/webpack/webpack/blob/master/lib/ExtendedAPIPlugin.js – Wouter Coebergh May 08 '18 at 14:53
0

That can be done with Webpack Stats Plugin. It gives you nice and neat output file with all the data you want. And it's easy to incorporate it to the webpack config files where needed.

E.g. To get hash generated by Webpack and use it elsewhere. Could be achieved like:

# installation
npm install --save-dev webpack-stats-plugin
yarn add --dev webpack-stats-plugin
# generating stats file
const { StatsWriterPlugin } = require("webpack-stats-plugin")

module.exports = {
  plugins: [
    // Everything else **first**.

    // Write out stats file to build directory.
    new StatsWriterPlugin({
      stats: {
        all: false,
        hash: true,
      },
      filename: "stats.json" // Default and goes straight to your output folder
    })
  ]
}
# usage
const stats = require("YOUR_PATH_TO/stats.json");

console.log("Webpack's hash is - ", stats.hash);

More usage examples in their repo

Hope that helps!

0

You could use https://www.npmjs.com/package/build-hash-webpack-plugin :

import BuildHashPlugin from 'build-hash-webpack-plugin';
// ...
module.exports = {
    // ....
    plugins: [new BuildHashPlugin()]
}

The output is a JSON object in the form:

{"hash":"68aaedf27867fc4cb95d"}
Réda Housni Alaoui
  • 1,244
  • 2
  • 15
  • 22