Using Htmlwebpackplugin with Webpack-Dev-Middleware

1k Views Asked by At

Short Summary

Is combining htmlwebpackplugin functionality with webpack-dev-middleware impossible because of dev-middleware's reliance on files in memory? Screenshots of script outputs at bottom of this post. Because I've chosen to implement cache-hashed filenames in my production config, I can't seem to use dev-middleware anymore.

My Setup

I have 2 main configurations for my webpack instance. One for development (with hot reload) and one for production. I utilize webpack-merge to create a common.config that I'll eventually extract commonalities between the two configurations (for now it's fairly blank). In terms of app setup I have an API run separately in Python.

The problem

On my production config I'm using splitchunks for vendor/bundle splitting as well as some minimizations. It works perfectly fine. However, when I'm trying to run my development environment, although it's creating the the appropriate bundles for development [i.e. without the hashing] according to the terminal, the index.html file is unable to be found (likely because webpack-dev-middleware looks for things in memory). As a result, I can't see my development environment and I can't see any of the hot reload changes? Previously I would generate all my bundle files with npm run build:production and then use NPM start. I imagine dev-middleware would overlay it's in-memory version of the bundle.js changes over my file on disk, but now that I'm using hashed filenames on prod I can't really do that anymore?

Package.json scripts

 "scripts": {
    "clean": "rimraf dist/*.{js,css,eot,woff,woff2,svg,ttf,jpg,map,json}",
    "build":
      "webpack -p --progress --verbose --colors --display-error-details --config webpack/common.config.js",
    "build:production": "npm run clean && npm run build",
    "flow": "flow",
    "lint": "eslint src",
    "start": "nodemon bin/server.js",

The relevant parts of server.js

    (function initWebpack() {
  const webpack = require('webpack');
  const webpackConfig = require('./webpack/common.config');
  const compiler = webpack(webpackConfig);

  app.use(
    require('webpack-dev-middleware')(compiler, {
      noInfo: true,
      publicPath: webpackConfig.output.publicPath,
    }),
  );

  app.use(
    require('webpack-hot-middleware')(compiler, {
      log: console.log,
      path: '/__webpack_hmr',
      heartbeat: 10 * 1000,
    }),
  );

  app.use(express.static(path.join(__dirname, '/')));
})();

app.get(/.*/, (req, res) => {
  res.sendFile(path.join(__dirname, '/dist/index.html'));
});

common.config.js

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

// const HtmlWebpackPlugin = require('html-webpack-plugin');

const development = require('./dev.config');
const production = require('./prod.config');

const TARGET = process.env.npm_lifecycle_event;

const PATHS = {
  app: path.join(__dirname, '../src'),
  build: path.join(__dirname, '../dist'),
  nodeModulesDir: path.join(__dirname, 'node_modules'),
  indexFile: path.join(__dirname, './src/index'),
};

process.env.BABEL_ENV = TARGET;

const common = {
  entry: [PATHS.app],

  output: {
    path: PATHS.build,
  },
};

if (TARGET === 'start' || !TARGET) {
  module.exports = merge(development, common);
}

if (TARGET === 'build' || !TARGET) {
  module.exports = merge(production, common);
}

dev.config.js

const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
require('babel-polyfill').default;

const HtmlWebpackPlugin = require('html-webpack-plugin');
const PATHS = {
  app: path.join(__dirname, '../src'),
};

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: ['webpack-hot-middleware/client', './src/index'],
  mode: 'development',

  output: {
    publicPath: '/dist/',
  },

  resolve: {
    extensions: ['.jsx', '.js', '.json', '.scss', '.less'],
    modules: ['node_modules', PATHS.app],
  },

  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
          'postcss-loader',
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
            },
          },
          'postcss-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
            },
          },
          'postcss-loader',
          {
            loader: 'less-loader',
            options: {
              //addlater
            },
          },
        ],
      },
      {
        test: /bootstrap-sass\/assets\/javascripts\//,
        use: [
          {
            loader: 'imports-loader',
            options: {
              jQuery: 'jquery',
            },
          },
        ],
      },
      {
        test: require.resolve('jquery'),
        use: [
          {
            loader: 'expose-loader',
            options: '$',
          },
          {
            loader: 'expose-loader',
            options: 'jQuery',
          },
        ],
      },
      {
        test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 50000,
              mimetype: 'application/font-woff',
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"development"',
      },
      __DEVELOPMENT__: true,
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.ProvidePlugin({
      jQuery: 'jquery',
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index_template.html',
    }),
  ],
};

Here's an example of what my index.html file looks like after using npm run build:production. As you can see, the in-memory version of index.html probably can't work with this anymore with the hashed filenames?

<link href="/dist/vendor.a85f.css" rel="stylesheet"><link href="/dist/main.4d1e.css" rel="stylesheet"></head>

<body>
    <div id="root"></div>
<script type="text/javascript" src="/dist/manifest.81a7.js"></script><script type="text/javascript" src="/dist/vendor.99aa.js"></script><script type="text/javascript" src="/dist/main.6eb4.js"></script></body>

Other notes:

  • On latest version of webpack 4.
  • My production version works fine

Any help much appreciated.

Output when I run build:production Output when I run npm start

UPDATE: I've swapped out rimraf dist for clean webpack plugin and moved it to my common.config. That way on each build it's doing the clean before generating index.html. However, I've noticed that when I use npm start, while the output in terminal is showing that files are emitted....I can't find them anywhere? After investigated webpack-dev-middleware, it seems they store things in memory. This is probably the core problem. How can I tie htmlwebpack plugin together with something like dev-middleware if it's in memory or perhaps I need to maintain a separate index.html file? I'm guessing the reason why this flow worked previously was because I had static names for bundle.js for both prod and dev so the in-memory version had no problem. now that the names are hashed from the prod version...it doesn't know what to do?

0

There are 0 best solutions below