0

I have three different Webpack configurations (client.webpack.js, server.webpack.js, and vendor.webpack.js) vendor.webpack.js is for creating a vendor bundle, as they change rarely and take up a lot of space. We use DllPlugin to generate it, and use the generated manifest.json in the client.webpack.js via the DllReferencePlugin.

The improvement I'd like to make is to add a hash to the vendor file, e.g. instead of vendor.js I want to create vendor.348723.js. The reason for that is to improve caching.

The tricky part is that our application is isomorphic, meaning that generation of HTML is done by the server (Node.js + React, server.webpack.js) in runtime. Inside the server, in a JS file, we create the template and there's <script type='text/javascript' src='/vendor.js'></script> somewhere. My question is, how do I inject the vendor.SOMEHASH.js there?

What I've tried and failed:

ExtendedAPIPlugin

Caching with Webpack, [hash] value inside index source code, using React.js

Using the ExtendedAPIPlugin inside the vendor.webpack.js, and trying to use __webpack_hash__ when generating the HTML template, e.g. <script type='text/javascript' src='/vendor.${__webpack_hash__}.js'></script> Since I have two different Webpack configurations, and that I generate the hash inside vendor.webpack.js, it is not recognized by the server when generating the HTML template.

Other relevant info

We don't use html-webpack-plugin -- I don't think it applies as we have an isomorphic app. ( Webpack - Best way to update HTML to include latest [hashed] bundles )

Other relevant pages I looked

Link css filename with hash to index.html after the Extract Text Plugin

vendor.webpack.js

const path = require('path')
const webpack = require('webpack')

module.exports = {
  name: 'vendor',
  mode: 'development',
  entry: [
    'axios',
    'babel-polyfill',
    'material-ui',
    'classnames',
    'mixpanel-browser',
    'ramda',
    'react',
    'react-dropzone-component',
    'react-dom',
    'react-ga',
    'react-helmet',
    'react-redux',
    'react-router-dom',
    'react-router-redux',
    'redux',
    'redux-thunk',
    'redux-saga'
  ],
  output: {
    path: path.resolve(__dirname, '../client'),
    filename: 'vendor.js',
    library: 'vendor_[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      name: 'vendor_[hash]',
      path: path.resolve(__dirname, '../client/manifest.json')
    }),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      }
    }),
    new webpack.ExtendedAPIPlugin(),
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
  ]
}

client.webpack.js

require('env2')('env.json')
const path = require('path')
const webpack = require('webpack')
const StatsPlugin = require('stats-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const merge = require('webpack-merge')
const baseConfig = require('./base.js')

module.exports = merge(baseConfig.moduleRules, {
  name: 'client',
  mode: 'development',
  target: 'web',
  // Good compromise between speed and quality, suitable for local development.
  devtool: 'cheap-module-eval-source-map',
  entry: [path.resolve(__dirname, '../app/index.js')],
  output: {
    filename: 'client.[chunkhash].js',
    chunkFilename: 'client.[chunkhash].js',
    path: path.resolve(__dirname, '../client'),
    publicPath: '/'
  },
  watchOptions: {
    poll: true
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'style.css'
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, '../client/manifest.json')
    }),
    new StatsPlugin('stats.json'),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('development')
      }
    })
  ],
  optimization: {
    runtimeChunk: 'single'
  }
})

server.webpack.js

const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const merge = require('webpack-merge')
const baseConfig = require('./base.js')

const res = p => path.resolve(__dirname, p)

const modeModules = res('../node_modules')
const entry = res('../lib/routes/resources/reactUrls.js')
const output = res('../buildServer')

// if you're specifying externals to leave unbundled, you need to tell Webpack
// to still bundle `react-universal-component`, `webpack-flush-chunks` and
// `require-universal-module` so that they know they are running
// within Webpack and can properly make connections to client modules:
const externals = fs
  .readdirSync(modeModules)
  .filter(x => !/\.bin|react-universal-component|webpack-flush-chunks/.test(x))
  .reduce((externals, mod) => {
    externals[mod] = `commonjs ${mod}`
    return externals
  }, {})

externals['react-dom/server'] = 'commonjs react-dom/server'

module.exports = merge(baseConfig.commons, {
  name: 'server',
  mode: 'development',
  target: 'node',
  // Good compromise between speed and quality, suitable for local development.
  devtool: 'cheap-module-eval-source-map',
  entry: [entry],
  externals,
  output: {
    path: output,
    filename: '[name].js',
    libraryTarget: 'commonjs2'
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '/style.css'
    }),
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    }),
    new webpack.EnvironmentPlugin(['NODE_ENV'])
  ]
})
kolistivra
  • 4,229
  • 9
  • 45
  • 58

1 Answers1

0

WebpackManifestPlugin can be used to fix this issue.

kolistivra
  • 4,229
  • 9
  • 45
  • 58