I have migrated Webpack v4 to v5, made changes relative to documentation and deprecation messages from CLI, and got successful build, though during the application testing I noticed that JavaScript doesn’t run and there are no errors. I am getting plain html rendered by SSR.
Have no clue why it doesn’t work, because it’s silent, maybe misconfiguration issue.
Here is my webpack configuration:
const webpack = require('webpack'); const path = require('path'); const glob = require('glob'); // webpack plugins const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const AssetsPlugin = require('assets-webpack-plugin'); // minification plugins const TerserJSPlugin = require('terser-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // image optimization plugins const ImageminPlugin = require("imagemin-webpack-plugin").default; const imageminGifsicle = require("imagemin-gifsicle"); const imageminPngquant = require("imagemin-pngquant"); const imageminSvgo = require("imagemin-svgo"); const imageminMozjpeg = require('imagemin-mozjpeg'); const ProgressPlugin = webpack.ProgressPlugin; const env = require('dotenv').config(); const isProd = process.env.NODE_ENV === 'production'; const isDev = !isProd; const environment = { NODE_ENV: process.env.NODE_ENV || 'development', CONFIG: process.env.CONFIG || 'development', DEBUG: process.env.DEBUG || false, }; const plugins = () => { const plugins = [ new CleanWebpackPlugin({ verbose: true, cleanStaleWebpackAssets: true, }), new webpack.EnvironmentPlugin(Object.assign(environment, env.parsed)), new MiniCssExtractPlugin({ filename: '[contenthash:5].[name].css', chunkFilename: '[chunkhash:5].[name].css', }), new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', moment: 'moment', _: 'lodash', }), new webpack.ContextReplacementPlugin(/moment[\/]locale$/, /^./(en)$/), new AssetsPlugin({ filename: 'assets.json', }), new ImageminPlugin({ cacheFolder: isDev ? path.resolve(__dirname, '../dist/cachedImages') : null, externalImages: { context: 'src', sources: glob.sync('src/assets/img/**/*.*'), destination: 'dist/img/', fileName: (filepath) => { let name = filepath.split(/img(/|\)/).pop(); return name; }, }, plugins: [ imageminGifsicle({ interlaced: false }), imageminMozjpeg({ progressive: true, arithmetic: false }), imageminPngquant({ floyd: 0.5, speed: 2 }), imageminSvgo({ plugins: [ { removeTitle: true }, { convertPathData: false } ] }), ], }), ]; if (isProd) { plugins.push( new ProgressPlugin({ entries: true, handler: (percentage, msg, ...args) => { let moduleName = ''; if (args[2]) { let sep = args[2].split(path.sep); let dirIndex = sep.indexOf('finstead-client'); moduleName = sep.slice(dirIndex).join('/'); } const currentWork = args[0] ? ` ${args[0]}` : ''; console.log(`[Progress${currentWork}]: (${(percentage * 100).toFixed(3)}%) ${msg} ${moduleName}`) } }), new BundleAnalyzerPlugin({ analyzerMode: 'static', reportFilename: path.resolve(__dirname, 'analysis.html'), generateStatsFile: false, logLevel: 'info', }) ); } return plugins; }; const optimization = () => { let optimizations = { moduleIds: isProd ? 'deterministic' : 'named', chunkIds: isProd ? 'deterministic' : 'named', splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\/]node_modules[\/](react|react-dom|lodash|moment)[\/]/, }, } } }; if (isProd) { optimizations.minimizer = [ new TerserJSPlugin({ terserOptions: { compress: { pure_funcs: ['console.log'], drop_console: true, drop_debugger: true }, warnings: false }, parallel: true }), new OptimizeCSSAssetsPlugin({}) ]; } return optimizations; }; const fontLoaders = [ { test: /.woff(?v=d+.d+.d+)?$/, use: { loader: 'url?limit=10000&mimetype=application/font-woff', } }, { test: /.woff2(?v=d+.d+.d+)?$/, use: { loader: 'url?limit=10000&mimetype=application/font-woff', } }, { test: /.ttf(?v=d+.d+.d+)?$/, use: { loader: 'url?limit=10000&mimetype=application/octet-stream', } }, { test: /.svg(?v=d+.d+.d+)?$/, use: { loader: 'url?limit=10000&mimetype=image/svg+xml', } }, { test: /.eot(?v=d+.d+.d+)?$/, use: { loader: 'file-loader', } } ]; const config = { mode: process.env.NODE_ENV, devtool: isProd ? false : 'eval-source-map', context: path.resolve(__dirname, '../src'), entry: { bundle: { import: './app.jsx', dependOn: 'vendor', }, styles: './sass/main.scss', vendor: { runtime: 'runtimecode', import: [ 'react', 'react-dom', 'redux', 'redux-saga', 'react-redux', 'react-router', 'react-tap-event-plugin', 'lodash', 'moment', ] } }, output: { publicPath: '/build/', filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js', path: path.resolve(__dirname, '../dist/build'), }, resolve: { extensions: ['.js', '.jsx', '.json'], alias: { config: path.resolve(__dirname, '../config.js'), utils: path.resolve(__dirname, '../src/utils'), shared: path.resolve(__dirname, '../src/components/shared'), services: path.resolve(__dirname, '../src/services'), store: path.resolve(__dirname, '../src/store'), constants: path.resolve(__dirname, '../src/constants'), actions: path.resolve(__dirname, '../src/actions'), components: path.resolve(__dirname, '../src/components'), }, }, optimization: optimization(), plugins: plugins(), module: { rules: [ { test: /.m?jsx?$/, resolve: { fullySpecified: false }, // ignore transpiling JavaScript from node_modules as they should be ready to go OOTB exclude: /node_modules/, }, { test: /.jsx?$/, exclude: [/node_modules/, /libs/], use: { loader: path.join(__dirname, '../helpers/custom-loader.js'), options: { presets: ['@babel/preset-env', '@babel/preset-react'], plugins: [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties', '@babel/plugin-transform-destructuring', '@babel/plugin-syntax-dynamic-import', '@babel/plugin-transform-runtime', 'syntax-async-functions' ], cacheDirectory: true } } }, { test: /.scss$/, use: [ { loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { url: false, } }, { loader: 'sass-loader', options: { implementation: require('node-sass'), } }, ] }, { test: /.css$/, use: ['style-loader', 'css-loader'] }, { test: /.(jpe?g|png|gif|svg)$/i, loader: "url-loader", options: { name: "[path][name].[ext]" }, }, ...fontLoaders ] }, watchOptions: { ignored: /node_modules/, }, }; module.exports = config;
Here the dev dependencies that I use
"devDependencies": { "@babel/cli": "^7.12.10", "@babel/core": "^7.12.10", "@babel/node": "^7.12.10", "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-decorators": "^7.12.12", "@babel/plugin-proposal-export-namespace-from": "^7.12.1", "@babel/plugin-proposal-function-sent": "^7.12.1", "@babel/plugin-proposal-json-strings": "^7.12.1", "@babel/plugin-proposal-numeric-separator": "^7.12.7", "@babel/plugin-proposal-object-rest-spread": "^7.12.1", "@babel/plugin-proposal-throw-expressions": "^7.12.1", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-destructuring": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.10", "@babel/preset-env": "^7.12.11", "@babel/preset-react": "^7.12.10", "@babel/register": "^7.12.10", "@babel/runtime": "^7.12.0", "@webpack-cli/migrate": "^1.1.2", "assets-webpack-plugin": "^7.0.0", "babel-eslint": "^10.0.3", "babel-loader": "^8.2.2", "babel-plugin-dynamic-import-webpack": "1.0.1", "babel-plugin-lodash": "^3.3.4", "babel-plugin-syntax-async-functions": "^6.13.0", "babel-plugin-system-import-transformer": "^3.1.0", "babel-plugin-transform-ensure-ignore": "^0.1.0", "babel-plugin-webpack-alias": "^2.1.2", "babel-watch": "^7.3.0", "clean-webpack-plugin": "^3.0.0", "cross-env": "^5.2.0", "css-loader": "^5.0.1", "eslint": "^6.8.0", "eslint-config-airbnb": "^18.2.1", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^1.7.0", "file-loader": "^1.1.11", "imagemin-mozjpeg": "^9.0.0", "imagemin-pngquant": "^9.0.1", "imagemin-svgo": "^8.0.0", "imagemin-webpack-plugin": "^2.4.2", "imports-loader": "^0.7.0", "mini-css-extract-plugin": "^1.3.3", "nodemon": "^2.0.7", "optimize-css-assets-webpack-plugin": "^5.0.4", "redux-devtools": "^3.7.0", "redux-devtools-dock-monitor": "^1.2.0", "redux-devtools-log-monitor": "^1.2.0", "resolve-url-loader": "^2.3.2", "sass-loader": "^10.1.0", "terser-webpack-plugin": "^5.0.3", "webpack": "^5.11.1", "webpack-bundle-analyzer": "^4.3.0", "webpack-cli": "^4.3.1" },
EDIT
I am suing assets-webpack-plugin
as you can see, which gives me JSON of entrypoints and chunks. I am manually inserting the bundle.[contenthash].js
and vendor.[contenthash].js
to my index.html which loads successfully.
But in source code of compiled bundles, I can see that bundle isn’t runtime executable function, but webpack module.
I have tried runtime
property on vendor
entrypoint, because main bundle
point is depended on vendor packages, even though it gave me executable function it doesn’t execute my main bundle code.
Advertisement
Answer
After more investigation I found, that besides entry bundles and runtime file, webpack
creates few more files needed for initial execution.
So for making custom html file or using any other preprocessor file we need to use either html-webpack-plugin
to generate html file based on optionally provided template or webpack-manifest-plugin
to get the list of the needed files in json file.
... plugins: [ new WebpackManifestPlugin({ fileName: path.resolve(__dirname, './manifest.json'), filter: (file) => file.isInitial, }), ] ...
Here with available filter option we process files.
filter
function provides FileDescriptor
object as an input for processing which containes isInitial
, isChunk
and few more fields which you can find in documentation
As you already guessed the files marked as isInitial
needed for running your application. Also you can generate custom manifest file using this plugin. For example I have a file where have separated initial css
and js
bundles.
{ "css": [ "/build/c3a7e.commons.css" ], "js": [ "/build/bundle.bf972748425cb97699f3.js", "/build/styles.09e2bc2ae43dc685a2c5.js", "/build/vendor.8e55b287c8b3ffc5c578.js", "/build/runtime.d9173e4531e516f18fc8.js", "/build/commons-app_jsx.1fc7ae7feba211c7a59d.js", "/build/vendor-node_modules_lodash_lodash_js-node_modules_moment_moment_js-node_modules_react-dom_index_js.fc6e3899340455918b6a.js", "/build/commons-node_modules_moment_locale_sync_recursive_en_-node_modules_react-redux_es_index_js-no-b2e9a0.a38dfe96c879814994af.js" ] }