2

Update: looks like this wasn't the cause at all - instead its a side-effect of some weird bodge code to bring a node server up in dev mode.. worked fine in webpack 2, but with weback 4 having the mode=prod/dev requirement - things work less than awesome, but at least I have found the cause now and this question should probably die quietly in embarrassment..


I've upgraded from webpack 2 to webpack 4 and things are messy with respect to development mode..

I have successfully built the app in production mode, but of course thats not very useful for me when debugging.. Production happily builds chunks and loads them correctly in the browser and things are good..

Development builds just main.js, loads it, but doesn't load any of the chunks. the chunks are there in the directory and the console doesn't show any errors.

The question is - what have I done wrong that is causing the chunks not to be loaded?

I'm calling things (for development) via package.json using

"postinstall": "npm run build:dll",
"prebuild": "npm run build:clean",
"build": "cross-env REACT_APP_VERSION=$npm_package_version NODE_ENV=production webpack --mode=development --config internals/webpack/webpack.dev.babel.js --color --progress",
"build-prod": "cross-env REACT_APP_VERSION=$npm_package_version NODE_ENV=production webpack --mode=production --config internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "npm run test:clean && rimraf ./build",
"build:dll": "node ./internals/scripts/dependencies.js",

My configs look like the following:-

webpack.dev.babel.js:

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

module.exports = require('./webpack.base.babel')({
  mode: 'development',
  entry: [
    path.join(process.cwd(), 'app/app.js'),
  ],

  output: {
    filename: '[name].js',
    chunkFilename: '[name].chunk.js',
    sourceMapFilename: '[file].map',
    publicPath: '/app/',
  },
  devtool: 'eval-source-map',
  plugins: [ 
    new HtmlWebpackPlugin({
      template: 'app/index.html',
      inject: true,
    }),
  ],

});

and webpack.base.babel.js:

/* eslint-disable */
/**
 * COMMON WEBPACK CONFIGURATION
 */

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

const env = {
  NODE_ENV: JSON.stringify(process.env.NODE_ENV),
  REACT_APP_VERSION: JSON.stringify(process.env.REACT_APP_VERSION),
};
const envConfig = dotenv.config({ path: `./.env.${process.env.NODE_ENV}` }).parsed;
for (const k in envConfig) {
  env[k] = envConfig[k];
}

module.exports = (options) => ({
  entry: options.entry,
  output: Object.assign({ // Compile into js/build.js
    path: path.resolve(process.cwd(), 'build'),
    publicPath: '/',
  }, options.output), // Merge with env dependent settings
  module: {
    rules: [{
      test: /\.js$/, // Transform all .js files required somewhere with Babel
      loader: 'babel-loader',
      include: /(app|internals|server)/,
      query: options.babelQuery,
    }, {
      // Do not transform vendor's CSS with CSS-modules
      // The point is that they remain in global scope.
      // Since we require these CSS files in our JS or CSS files,
      // they will be a part of our compilation either way.
      // So, no need for ExtractTextPlugin here.
      test: /\.css$/,
      include: /node_modules/,
      loaders: ['style-loader', 'css-loader'],
    }, {
      test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
      loader: 'url-loader?limit=10000&mimetype=image/svg+xml',
    }, {
      test: /\.(eot|ttf|woff|woff2)$/,
      loader: 'url-loader?limit=100000',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        'file-loader',
        {
          loader: 'image-webpack-loader',
          query: {
            progressive: true,
            optimizationLevel: 7,
            interlaced: false,
            pngquant: {
              quality: '65-90',
              speed: 4,
            },
          },
        },
      ],
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader',
      query: {
        limit: 10000,
      },
    }],
  },
  plugins: options.plugins.concat([
    new webpack.ProvidePlugin({
      // make fetch available
      fetch: 'exports-loader?self.fetch!whatwg-fetch',
    }),

    // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
    // inside your code for any environment checks; UglifyJS will automatically
    // drop any unreachable code.
    new webpack.DefinePlugin({
      'process.env': env,
    }),
    //new webpack.NamedModulesPlugin(), //webpack4 does this for us now
    new webpack.IgnorePlugin(/^\.\/lang$/, /moment$/),
  ]),
  resolve: {
    modules: ['app', 'node_modules'],
    mainFields: [
      'browser',
      'jsnext:main',
      'main',
    ],
    alias: {
      moment$: 'moment/moment.js',
    },
  },
  devtool: options.devtool,
  target: 'web', // Make web variables accessible to webpack, e.g. window
  node: {
    fs: 'empty'
  },
  performance: options.performance || {},
});

Update: I found that if I set --mode=production then I get things working.. and by inserting devtool: 'cheap-module-source-map', I get map files - so I can kinda do debugging.. but it doesn't explain why when I set the mode to development nothing works..

prod webpack : called identically from package.json except for the config file

// Important modules this config uses
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OfflinePlugin = require('offline-plugin');

module.exports = require('./webpack.base.babel')({
  // In production, we skip all hot-reloading stuff
  entry: [
    path.join(process.cwd(), 'app/app.js'),
  ],

  // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
  output: {
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].chunk.js',
    publicPath: '/app/',
  },

  plugins: [
    //new webpack.optimize.CommonsChunkPlugin({
    //  name: 'vendor',
    //  children: true,
    //  minChunks: 2,
    //  async: true,
    //}),

    // Minify and optimize the index.html
    new HtmlWebpackPlugin({
      template: 'app/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
      inject: true,
    }),

    // Put it in the end to capture all the HtmlWebpackPlugin's
    // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
    new OfflinePlugin({
      relativePaths: false,
      publicPath: '/app/',

      // No need to cache .htaccess. See http://mxs.is/googmp,
      // this is applied before any match in `caches` section
      excludes: ['.htaccess'],

      caches: {
        main: [':rest:'],

        // All chunks marked as `additional`, loaded after main section
        // and do not prevent SW to install. Change to `optional` if
        // do not want them to be preloaded at all (cached only when first loaded)
        additional: ['*.chunk.js'],
      },

      // Removes warning for about `additional` section usage
      safeToUseOptionalCaches: true,

      AppCache: false,

      updateStrategy: 'changed',
      autoUpdate: 1000 * 60 * 2,
      externals: ['/'],
      ServiceWorker: {
        events: true,
        navigateFallbackURL: '/',
        publicPath: '/sw.js',
      },
    }),
  ],

  performance: {
    assetFilter: (assetFilename) => !(/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename)),
  },
});

dependencies.js

// No need to build the DLL in production
if (process.env.NODE_ENV === 'production') {
  process.exit(0);
} 

require('shelljs/global');

const path = require('path');
const fs = require('fs');
const exists = fs.existsSync;
const writeFile = fs.writeFileSync;

const defaults = require('lodash/defaultsDeep');
const pkg = require(path.join(process.cwd(), 'package.json'));
const config = require('../config');
const dllConfig = defaults(pkg.dllPlugin, config.dllPlugin.defaults);
const outputPath = path.join(process.cwd(), dllConfig.path);
const dllManifestPath = path.join(outputPath, 'package.json');

/**
 * I use node_modules/react-boilerplate-dlls by default just because
 * it isn't going to be version controlled and babel wont try to parse it.
 */
mkdir('-p', outputPath);

echo('Building the Webpack DLL...');

/**
 * Create a manifest so npm install doesn't warn us
 */
if (!exists(dllManifestPath)) {
  writeFile(
    dllManifestPath,
    JSON.stringify(defaults({
      name: 'react-boilerplate-dlls',
      private: true,
      author: pkg.author,
      repository: pkg.repository,
      version: pkg.version,
    }), null, 2),
    'utf8'
  );
}

echo ('Made manifest - building DLL');

// the BUILDING_DLL env var is set to avoid confusing the development environment
exec('cross-env BUILDING_DLL=true webpack --mode=development --display-chunks --color --config internals/webpack/webpack.dll.babel.js');

config for dependencies.js

const resolve = require('path').resolve;
const pullAll = require('lodash/pullAll');
const uniq = require('lodash/uniq');

const ReactBoilerplate = {
  // This refers to the react-boilerplate version this project is based on.
  version: '3.7.0',

  /**
   * The DLL Plugin provides a dramatic speed increase to webpack build and hot module reloading
   * by caching the module metadata for all of our npm dependencies. We enable it by default
   * in development.
   *
   *
   * To disable the DLL Plugin, set this value to false.
   */
  dllPlugin: {
    defaults: {
      /**
       * we need to exclude dependencies which are not intended for the browser
       * by listing them here.
       */
      exclude: [
        'bootstrap-css-only',
        'chalk',
        'compression',
        'cross-env',
        'express',
        'ip',
        'minimist',
        'sanitize.css',
      ],

      /**
       * Specify any additional dependencies here. We include core-js and lodash
       * since a lot of our dependencies depend on them and they get picked up by webpack.
       */
      include: ['core-js', 'lodash'],
      //'eventsource-polyfill', 'babel-polyfill', - removed since core-js 'usage' now handles this

      // The path where the DLL manifest and bundle will get built
      path: resolve('../node_modules/react-boilerplate-dlls'),
    },

    entry(pkg) {
      const dependencyNames = Object.keys(pkg.dependencies);
      const exclude = pkg.dllPlugin.exclude || ReactBoilerplate.dllPlugin.defaults.exclude;
      const include = pkg.dllPlugin.include || ReactBoilerplate.dllPlugin.defaults.include;
      const includeDependencies = uniq(dependencyNames.concat(include));

      return {
        reactBoilerplateDeps: pullAll(includeDependencies, exclude),
      };
    },
  },
};

module.exports = ReactBoilerplate;
6
  • it suspect it has something to do with webpack's new split strategy which has a min size of 30kb by default. maybe you can try setting optimization. splitChunks.minSize: 0 and see if that makes a difference in dev mode webpack.js.org/plugins/split-chunks-plugin/#defaults Commented Mar 4, 2020 at 21:03
  • According to the docs webpack4 provide 3 ways of code splitting: by entry points (you have only 1), using SplitChunksPlugin (but I see no optimization section in your config), and dynamic imports (do you have it in your source?). So maybe webpack doesn't produce chunks at all, only the entry file chunk? And for prod you are using old chunks from previous build? Commented Mar 5, 2020 at 21:33
  • Also can you add please your prod webpack config too? Commented Mar 5, 2020 at 21:36
  • added optimization: { splitChunks: {minSize: 0 } }, to the dev config after devtools - it builds, but doesn't display things.. I'm really at a loss why changing the mode should change the way things run - but I did do more thinking and noticed that it always does a build:dll beforehand.. and build :dll calls a different script and a different config.. (yay for overly complex builds..) - will add these too.. Commented Mar 6, 2020 at 0:19
  • slowly digging through - it might be something else all together.. I've noticed some prod vs dev code in the node server stuff.. Commented Mar 6, 2020 at 2:30

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.