Search code examples
javascriptsasswebpackfrontendwebpack-2

Content Hashes, ExtractTextPlugin and HtmlWebpackPlugin


I guess I'll start with my webpack config.

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

/**
 * Environment
 */
const nodeEnv = process.env.NODE_ENV || 'development';
const isProduction = nodeEnv === 'production';
const isDevelopment = !isProduction;

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const sourcePath = path.join(__dirname, 'assets');
const buildPath = path.join(__dirname, 'dist');

const extractSass = new ExtractTextPlugin({
  filename: '[name].[contenthash].css',
  disable: isDevelopment
});

/**
 * Plugins
 */
const plugins = [
  new HtmlWebpackPlugin({
    template: path.join(sourcePath, 'index.html'),
  }),
  extractSass
];

if (isProduction) {
} else {
  plugins.concat([
    new webpack.HotModuleReplacementPlugin(),
  ]);
}

module.exports = {
  entry: ['./assets/app.js', './assets/app.scss'],
  devtool: isProduction ? 'eval' : 'source-map',
  plugins: plugins,
  module: {
    rules: [{
      test: /\.scss$/,
      use: extractSass.extract({
        use: [{
          loader: "css-loader"
        }, {
          loader: "sass-loader"
        }],
        // use style-loader in development
        fallback: "style-loader"
      })
    }]
  },
  output: {
    filename: 'bundle.js',
    path: buildPath
  },
  devServer: {
    contentBase: buildPath,
    port: 9000
  }
};

This all works fine when running on the webpack dev server but I'm trying to figure out how this fits together on a production environment.

As you can see, as per the sass-loader documentation, I'm creating a file called [name].[contenthash].css if NODE_ENV is set to production. I love the idea of serving files based on the content hash because I love integrity.

The difficulty I'm having is understanding how I can pass that file name, that content hash into the index.html template I'm creating so that I can <link> the stylesheet.

  • Is it a server side thing?
  • Is there any way to pass that file name into the HTML template on production?
  • Is it intentional that I do it manually or script it out?

I just don't understand how these two components come together to produce a publishable build. HtmlWebpackPlugin produced a .html in the output directory but obviously it has no innate understanding of where to find it's styles.


Solution

  • Your config seems correct.

    Is there any way to pass that file name into the HTML template on production?

    What should be happening is that the HtmlWebpackPlugin should be creating a new index.html file in your buildPath directory, which has the generated bundles automatically injected in it (for example the generated CSS bundle will be injected in the head tag and the generated script bundles at the bottom of the body tag)

    Beyond that it is just a matter of serving that dist/index.html to whoever visits your site/app. So the answer to

    Is it a server side thing?

    is yes.

    Try doing a build without the dev server, by simply running webpack, so you can see the output of your configuration (the dev server builds things in memory, so you do not actually get to see them)