Webpack code splitting impacts web performance - javascript

I have a React/Node + SSR application, I am trying to create a production build, I have managed to do that but the problem is that the files that I have in the build are too large.
I use latest version of react + webpack 4.
Here is my webpack configuration:
clientConfig.js
const path = require('path');
const common = require('./webpack.common-config');
const clientConfig = {
...common,
mode: 'production',
name: 'client',
target: 'web',
devtool: false,
entry: {
client: [
'#babel/polyfill',
'./src/client.js'
],
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
chunks: 'all',
name: 'vendor',
test: module => /node_modules/.test(module.resource),
enforce: true
},
common: {
chunks: 'all',
name: 'client'
}
},
},
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
}
};
module.exports = clientConfig;
serverConfig.js
const nodeExternals = require('webpack-node-externals');
const path = require('path');
const common = require('./webpack.common-config');
const serverConfig = {
...common,
mode: 'production',
name: 'server',
target: 'node',
devtool: false,
externals: [nodeExternals()],
entry: {
server: ['#babel/polyfill', path.resolve(__dirname, 'src', 'server.js')],
},
optimization: {
splitChunks: {
chunks: 'all',
}
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js',
chunkFilename: "[id].chunk.js"
},
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
};
module.exports = serverConfig;
commonConfig.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const common = {
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, 'src')],
query: {
presets: [
['#babel/preset-env', {loose: true, modules: false}],
"#babel/preset-react"
],
plugins: [
"#babel/plugin-proposal-class-properties"
]
},
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader'
]
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
]
}
]
},
plugins: [
new OptimizeCSSAssetsPlugin(),
new MiniCssExtractPlugin({
filename: "styles.css",
})
],
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
},
};
module.exports = common;
And another file which basically merges the client and the server config.
I run npm run build after that I run webpack -p --mode=production --optimize-minimize && node ./build/server.js
I get the following warning:
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
vendor.js (667 KiB)
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
client (928 KiB)
vendor.js
styles.css
client.js
Any advice or idea for the above size warning would be great! Thank you!

I recommend that you try core-js instead of babel/pollyfill this can help you to reduce your bundle size.
Also, I recommend that you try dynamic importing with react-loadable which supports SSR. there are different strategies for splitting your code. you can read more here(most important one)
In case you are using CSS frameworks such as bootstrap you should only use parts that you need and avoid importing all of them. there is a tool for purging unused CSS named purgecss but use it with caution and you have to know what are you doing.
In case you are using libraries such as lodash or material-ui you should import your module specifically to avoid importing all the packages into your bundle.
exp:
import debounce from 'lodash/debounce'
npm dedup or yarn dedup can help to remove duplicate dependencies inside bundle.

You may be able to get some reduction through adjusting your babel configuration. For instance, specifying some options for the "preset-env", such as bugfixes, targets if you don't have support older browsers, and using corejs polyfills instead of babel/polyfill.
"#babel/preset-env", {
"targets": {
"browsers": [
"last 2 years"
]
},
"bugfixes": true,
"useBuiltIns": "entry", // experiment with "entry" vs. "usage"
"corejs": 3
...
Depending on your codebase, the babel-plugin-transform-runtime may help too. I had some projects where it made a substantial difference, but other where it hasn't.
"#babel/plugin-transform-runtime",
{
"corejs": 3,
"version": "7.9.2"
}
Additional options for Webpack include using the webpack-cdn-plugin. This can greatly reduce your vendor bundle size. Of course, users will still have to download those same libraries, but they will be cached and don't need to re-downloaded every time your bundle changes or updates.
A little additional savings can be had by specifying runtimeChunk: true, and optionally inlining that chunk in your index.html with
new HTMLWebpackPlugin({
inlineSource: 'runtime~.+[.]js',
...
and the html-webpack-inline-source-plugin or similar.

You can split files to lower size to remove warning using splitChunks:
optimization: {
splitChunks: {
chunks: 'all',
minSize: 10000,
maxSize: 250000,
}
}
Check more for minSize and maxSize.
If you want to disable the warning, you can do it using performance
performance: {
hints: false
}
or remove warning for certain size limit like 1 MB:
performance: {
maxAssetSize: 1000000
}

I managed to switch to Next.js so all of this is not necessary anymore.

Related

Node project: trying to use the same ES6 code for both the front end and back end

I have a need to share front end and back end code in a single javascript project. The front end code is transpiled using babel and the back end code is a node script (not a web app) that runs on the server.
There's currently just a single package.json file in the project.
I'm using --experimental-vm-modules to run the node server script which allows me to use ES6 syntax and import statements, however it requires the javascript files to have an .mjs extension.
The catch-22 I'm running into is that the file that is meant to be shared between the FE and BE (sharedConstants.js) only works on the back end with an .mjs extension and only works on the client side with a .js extension.
Another solution I considered was to put 'type: module' in package.json file, but then this breaks the compilation of the client side app which uses webpack, babel, react, etc.
The project is currently using node version v14.18.0 but I'm open to considering an upgrade if that in some way helps to resolve this issue.
UPDATE:
Webpack version is 4.42.0 and #babel/core version is 7.2.2
(also added contents of .babelrc, webpack.config.babel.js and webpack.config.common.js)
Contents of .babelrc:
{
"plugins": [
"#babel/proposal-class-properties",
"#babel/plugin-syntax-dynamic-import",
"#babel/transform-runtime",
["import", { "libraryName": "antd", "libraryDirectory": "lib"}, "antd"],
["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib"}, "antd-mobile"],
["babel-plugin-webpack-alias", { "config": "./webpack.config.common.js" }]
],
"presets": [
"#babel/preset-react", "#babel/preset-env"
],
"only": [
"./**/*.js",
"node_modules/jest-runtime"
],
"sourceType": "module"
}
Contents of webpack.config.babel.js:
let webpack = require('webpack');
let webpackCommon = require('./webpack.config.common.js');
module.exports = Object.assign({}, webpackCommon, {
mode: 'development',
entry: [
'./src/app-client.js',
/* The port on the development server must be entered in this section to
work properly. */
'webpack-dev-server/client?http://0.0.0.0:4000',
'webpack/hot/only-dev-server'
],
output: {
// Files are being written to disk so it's not certain at this point if
// they are being served from memory in a development environment.
// TODO: Confirm the performance impact of disk vs memory and if with the
// current configuration the files are being served from memory.
path: __dirname + '/public/',
filename: 'bundle.js'
},
devtool: 'source-map',
// dev server settings
devServer: {
hot: true,
filename: 'bundle.js',
publicPath: '/',
historyApiFallback: true,
contentBase: './public/index-static.html',
/* all requests are proxied to receive the response,
if it is bundle file, program uses script of dev server. */
proxy: {
'**': 'http://localhost:5000' // express server address
},
stats: {
// minimize console log
assets: false,
colors: true,
version: false,
hash: false,
timings: false,
chunks: false,
chunkModules: false
}
}
});
module.exports.plugins.push(new webpack.HotModuleReplacementPlugin());
Contents of webpack.config.common.js:
// Common webpack configuration.
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const fs = require('fs');
const lessToJs = require('less-vars-to-js');
const antdThemeVarsOverride = lessToJs(
fs.readFileSync(
path.join(__dirname, './src/static/less/antd-theme-vars-override.less'),
'utf8'
)
);
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: ['./src/app-client.js', './src/static/less/main.less'],
output: {
path: __dirname + '/public/',
filename: 'bundle.js'
},
module: {
// NOTE! If you change the order of the rules, the end of the prod
// webpack config file must be changed as well!
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{loader: 'react-hot-loader/webpack'},
{
loader: 'babel-loader',
options: JSON.stringify({
babelrc: false,
cacheDirectory: true,
presets: ['#babel/preset-env', '#babel/react'],
plugins: [
'#babel/transform-runtime',
'#babel/proposal-class-properties',
'lodash',
['import', {libraryName: 'antd', style: true}, 'antd'],
[
'import',
{libraryName: 'antd-mobile', style: true},
'antd-mobile'
]
]
})
}
]
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader']
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader']
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.less$/,
use: [
{loader: MiniCssExtractPlugin.loader},
{loader: 'css-loader'},
{
loader: 'less-loader',
options: {
modifyVars: antdThemeVarsOverride,
javascriptEnabled: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
},
resolve: {
modules: ['node_modules', path.join(__dirname, 'src')],
alias: {
Actions: path.resolve(__dirname, 'src/actions/'),
Components: path.resolve(__dirname, 'src/components/'),
Containers: path.resolve(__dirname, 'src/containers/'),
Db: path.resolve(__dirname, 'server/db/'),
Table: path.resolve(__dirname, 'src/components/HipocampoTable/'),
Js: path.resolve(__dirname, 'src/js/'),
Layouts: path.resolve(__dirname, 'src/components/layouts/'),
Reducers: path.resolve(__dirname, 'src/reducers/'),
Selectors: path.resolve(__dirname, 'src/selectors/'),
Server: path.resolve(__dirname, 'server/'),
Scripts: path.resolve(__dirname, 'scripts/'),
Spreadsheets: path.resolve(__dirname, 'spreadsheets/'),
Shared: path.resolve(__dirname, 'src/shared/'),
Slices: path.resolve(__dirname, 'src/slices/'),
Static: path.resolve(__dirname, 'src/static/'),
Support: path.resolve(__dirname, 'cypress/support/'),
modernizr$: path.resolve(__dirname, '.modernizrrc')
},
extensions: ['.js', '.json', '.less', '.jsx', '.css', '.sql']
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{from: './static/favicon.png', to: 'favicon.png'},
{from: './static/circle_logo.png', to: 'circle_logo.png'}
]
}),
new HTMLWebpackPlugin({
title: 'Hipocampo',
template: './static/index-template.html',
filename: 'index-static.html',
// This is needed to ensure that the password reset link works.
publicPath: '/',
minify: {
collapseWhitespace: false,
removeComments: false
}
}),
// This is to reduce the moment.js webpack size
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en-US/),
new MiniCssExtractPlugin()
]
};

Webpack Tree-Shaking Dynamic Imports seems not to be working

History:
I recently discovered an odd behaviour with using Webpack and dynamic imports. First I thought it might be the 3rd-party library 'Loadable Components' I used, so I opened a bug issue (https://github.com/gregberge/loadable-components/issues/517) on their end. The author replied telling me that the behaviour is coming from Webpack and the dynamic imports themselves.
I can stand the fact that it does not tree-shake the dynamic import, for me it is more important to understand why that is the case.
Demo repository to demonstrate the behaviour can be found here: https://github.com/dazlious/treeshaking-dynamic-imports
Short description of the problem: From my perspective, an imported named export should not force all the exported code to be bundled within it.
In the demo case we have a component (./lib/index.jsx) that has two sub components called module1 (./lib/module1/module1.jsx) and module2 (./lib/module1/module2.jsx). Module1 exports a constant called FOO_BAR that is then imported by Module2 as a named import.
When looking at the build output, you'll find Module2 containing Module1 in whole and not only the string that is specifically imported.
Is anyone with deep knowledge of Webpack and/or dynamic imports around here? Would be happy to learn more about the behaviour.
I edited the webpack.config to be:
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const baseDir = path.resolve(__dirname);
const config = {
mode: process.env.NODE_ENV,
stats: 'minimal',
resolve: {
extensions: ['.js', '.jsx'],
symlinks: false,
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'analyze.html',
}),
],
target: 'web',
devtool: 'hidden-source-map',
entry: {
bundle: [path.resolve(baseDir, 'lib')],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
mangleWasmImports: true,
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
name: 'vendor',
chunks: 'all',
test: /node_modules/,
priority: 20
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
priority: 10,
reuseExistingChunk: true,
enforce: true
}
}
},
},
output: {
chunkFilename: '[name].[chunkhash].js',
publicPath: '/',
path: path.join(baseDir, 'dist'),
filename: '[name].[hash].js',
},
module: {
rules: [
{
test: /^.*\.jsx?$/,
include: [path.resolve(baseDir, 'lib')],
loader: 'babel-loader?cacheDirectory',
},
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
],
},
};
module.exports = config;
I think this has the result you are looking for?
image of bunde analyzer showing modules in their own bundles
I think it requires the splitChunks option to actually tree-shake the components properly.
I have spend a lot of time trying to figure webpack out, but I'm still guessing here.

Why does webpack ignore chunk in production mode?

I have multiple entry points that share same code. What I need to do is to extract this code into one file using splitCode in webpack 4. This works fine in development mode but not in production.
Configuration file:
var path = require('path');
const ManifestPlugin = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const WebpackMd5Hash = require("webpack-md5-hash");
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: "development",
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
},
{
test: /\.(sass|scss|css)$/,
use: [
"style-loader",
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
}
],
},
output: {
path: path.join(__dirname, 'public'),
filename: 'js/[name]-[chunkhash].js',
chunkFilename: 'js/[name]-[chunkhash].js',
publicPath: '/'
},
externals: {
jquery: "jQuery"
},
optimization: {
runtimeChunk: "single",
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all",
priority: 1
},
utilities: {
test: /\.s?js$/,
minChunks: 2,
name: "utilities",
chunks: "all",
priority: 0
}
}
}
},
context: path.join(__dirname, 'resources/assets'),
entry: {
a: './js/a.js',
b: './js/b.js'
},
plugins: [
new CleanWebpackPlugin(['public/js/*.*', 'public/css/*.*'], {} ),
new MiniCssExtractPlugin({
filename: "css/[name]-[contenthash].css"
}),
new WebpackMd5Hash(),
new ManifestPlugin({
fileName: 'manifest.json'
}),
]
};
In development mode Webpack creates two entry points, one runtime.js, vendor.js and utilities.js which is ok.
When I change mode from development to production, webpack ignores utilities cacheGroups and appends common codebase into two entry points.
What am I missing?
Webpack version: 4.28.4
Node version: 8.15
It seems like setting enforce to true does the job (but I'm not entirely really sure why).
It should be like this:
utilities: {
test: /\.s?js$/,
minChunks: 2,
name: "utilities",
chunks: "all",
priority: 0,
enforce: true
}
From now on, utilities.js is being created not only in development mode, but also in production.
It's not a bug. It's a feature
Webpack 4 splitchunks.cacheGroups is ignored in production mode IF the new chunk is less than the size of 30kb.
solution to override this default condition:
user splitchunks.cacheGroups.runtime.enforce: true if you want to really make sure that these chunks are created
Check the documention for further details https://webpack.js.org/plugins/split-chunks-plugin/#defaults
Specifying minChunks: 2 means it will only create a split bundle if the given common imports is specified in at least 2 modules. You might want to verify but dropping it to 1.
There are few additional default rules listed here: https://gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693#defaults but mainly if the common codebase isn't larger than 30kb (before min+gz) then it won't get split out. You can force it by updated the key minSize as listed in the default optimization config.
Webpack SplitChunksPlugin, by default, ignores any chunk smaller than 30kb. If you run Webpack in development mode, you'll be able to see the bundle size of utilities.js and enforce the split by setting optimization.splitChunks.minSize option smaller than the size of utilities.js.

Webpack, html-webpack-plugin, Error: Child compilation failed

I 've got a problem with my webpack configuration. After implementing html-webpack-plugin I got an Error, there's whole error stack from generated index.html.
Error Stack:
Html Webpack Plugin:
Error: Child compilation failed:
Conflict: Multiple assets emit to the same filename index.html:
Error: Conflict: Multiple assets emit to the same filename index.html
compiler.js:76
[Pre-build]/[html-webpack-plugin]/lib/compiler.js:76:16
Compiler.js:291 Compiler.
[Pre-build]/[webpack]/lib/Compiler.js:291:10
Compiler.js:494
[Pre-build]/[webpack]/lib/Compiler.js:494:13
Tapable.js:138 next
[Pre-build]/[tapable]/lib/Tapable.js:138:11
CachePlugin.js:62 Compiler.
[Pre-build]/[webpack]/lib/CachePlugin.js:62:5
Tapable.js:142 Compiler.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:142:13
Compiler.js:491
[Pre-build]/[webpack]/lib/Compiler.js:491:10
Tapable.js:131 Compilation.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:131:46
Compilation.js:645 self.applyPluginsAsync.err
[Pre-build]/[webpack]/lib/Compilation.js:645:19
Tapable.js:131 Compilation.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:131:46
Compilation.js:636 self.applyPluginsAsync.err
[Pre-build]/[webpack]/lib/Compilation.js:636:11
Tapable.js:131 Compilation.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:131:46
Compilation.js:631 self.applyPluginsAsync.err
[Pre-build]/[webpack]/lib/Compilation.js:631:10
Tapable.js:131 Compilation.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:131:46
Compilation.js:627 sealPart2
[Pre-build]/[webpack]/lib/Compilation.js:627:9
Tapable.js:131 Compilation.applyPluginsAsyncSeries
[Pre-build]/[tapable]/lib/Tapable.js:131:46
Compilation.js:575 Compilation.seal
[Pre-build]/[webpack]/lib/Compilation.js:575:8
Compiler.js:488
[Pre-build]/[webpack]/lib/Compiler.js:488:16
Tapable.js:225
[Pre-build]/[tapable]/lib/Tapable.js:225:11
Compilation.js:477 _addModuleChain
[Pre-build]/[webpack]/lib/Compilation.js:477:11
Compilation.js:448 processModuleDependencies.err
[Pre-build]/[webpack]/lib/Compilation.js:448:13
next_tick.js:73 _combinedTickCallback
internal/process/next_tick.js:73:7
next_tick.js:104 process._tickCallback
internal/process/next_tick.js:104:9
My webpack configuration code:
var webpack = require('webpack'),
path = require('path');
var CopyWebpackPlugin = require('copy-webpack-plugin'),
ExtractTextWebpackPlugin = require('extract-text-webpack-plugin'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
const sourcePath = path.resolve(__dirname, './src');
const staticPath = path.resolve(__dirname, './static');
module.exports = function (env) {
const nodeEnv = env && env.prod ? 'production' : 'development';
const isProd = nodeEnv === 'production';
const postcssLoader = {
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}
const plugins = [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity,
filename: 'vendor.bundle.js'
}),
new webpack.EnvironmentPlugin({
NODE_ENV: nodeEnv,
}),
new HtmlWebpackPlugin({
template: 'index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
})
];
if(isProd) {
plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
},
output: {
comments: false,
},
})
);
} else {
plugins.push(
new webpack.HotModuleReplacementPlugin()
);
}
return {
devtool: isProd? 'source-map' : 'eval',
context: sourcePath,
entry: {
app: './app/entry.ts',
vendor: './app/vendor.ts'
},
output: {
path: staticPath,
filename: '[name].bundle.js',
},
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
use: {
loader: 'file-loader',
query: {
name: '[name].[ext]'
},
},
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
'ts-loader'
],
},
],
},
resolve: {
alias: {
Public: path.resolve(__dirname,'src/public'),
Style: path.resolve(__dirname,'src/styles')
},
extensions: ['.ts','.js', '.html'],
modules: [
path.resolve(__dirname, 'node_modules'),
sourcePath
]
},
plugins,
performance: isProd && {
maxAssetSize: 100,
maxEntrypointSize: 300,
hints: 'warning'
},
stats: {
colors: {
green: '\u001b[32m'
}
},
devServer: {
contentBase: './src',
historyApiFallback: true,
port: 3000,
compress: isProd,
inline: !isProd,
hot: !isProd,
stats: {
assets: true,
children: false,
chunks: false,
hash: false,
modules: false,
publicPath: false,
timings: true,
version: false,
warnings: true,
color: {
green: '\u001b[32m'
}
},
}
};
};
I couldn't find any source of that Error, maybe I am little bit tired, but I would like to finish it up, so I hope for your help guys.
Maybe should I use some raw-loader to load .html(?), which does not make me happy.
The problem is indeed the file-loader, because it simply copies the file over. By the time html-webpack-plugin tries to write index.html it has already been written by file-loader, hence resulting in a conflict.
There are several ways to resolve that issue, depending on what your needs are.
You could use html-loader for your HTML, although if you expect your imported HTML to simply be copied, it isn't the correct choice. To be clear, by the imported HTML I don't mean the template used by the html-webpack-plugin.
If you want to keep using the file-loader for your other HTML files, you can exclude the index.html so html-webpack-plugin falls back to its default loader. require.resolve works like require but gives you the full path of the module instead of its content.
{
test: /\.html$/,
exclude: [/node_modules/, require.resolve('./index.html')],
use: {
loader: 'file-loader',
query: {
name: '[name].[ext]'
},
},
},
When no loader matches the template, the html-webpack-plugin uses an ejs loader as a fallback. If you don't need any loader for .html files, you could remove the rule entirely and it would work just fine. That is rather unlikely, otherwise you wouldn't have a .html rule in the first place, but this also means you can use the .ejs extension to not apply the .html rule, as all HTML is valid EJS. You would rename index.html to index.ejs and change your plugin configuration accordingly:
new HtmlWebpackPlugin({
template: 'index.ejs',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
chunksSortMode: 'dependency'
})
I had to upgrade my node version. then the issue is fixed.
upgrade to latest node version(ubuntu)
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
sudo n latest
to check the version
node -v
you might need to restart your terminal to see the latest version.
For my case I had the template file name misspelled.
Solution to my problem was updating node version.
This is may be not ordinary case, but for me, problem was caused by symbol "!" in the path. After I moved all content to the folder without "!" then the error disappeared.
delete node modules package then run npm install all package install then npm start the same problem are face but this process to run solve the problem so this solution is useful
I had the same issue few minutes ago and I switched to a stable version of node to solve it. It worked
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
Simple Updated Node JS Version. And use
npm i
this will update your all libraries
This method works for me you can also try

Uncaught ReferenceError: require is not defined - Webpack2

I'm updating an app to use webpack 2 from webpack 1 and the regular build works fine. The issue seems to arise when using the devServer and requiring just one of the chunks generated (it's an electron app so I have main and renderer chunks - both are included in a regular build, and with the dev server only the renderer chunk is included)
Everything worked on webpack 1, but for some reason the runtime isn't being included in my chunks? I've tried reordering them but to no avail.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = {
context: __dirname,
entry: {
main: './main.js',
renderer: './app/index.jsx'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: "[name].js"
},
node: {
__dirname: false,
__filename: false
},
devtool: 'cheap-eval-source-map',
target: 'electron',
module: {
loaders: [
{
test: /(\.js$|\.jsx$)/,
exclude: /(node_modules|dist)/,
loader: 'babel'
},
{ test: /\.scss$/, loader: "style!css?modules!sass" },
{ test: /\.png$/, loader: "url?limit=100000" },
{ test: /\.jpg$/, loader: "file" }
]
},
resolve: {
extensions: ['.js', '.jsx', '.scss'],
modules: [path.resolve('./app'), 'node_modules']
},
plugins: [
new HtmlWebpackPlugin({
template: './app/index.html',
chunks: ['renderer'],
inject: 'body',
hash: 'true'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"dev"'
}
})
],
devServer: {
contentBase: __dirname
},
externals: {
'cheerio': 'window',
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
}
};
It seems one of the chunks isn't included properly:
/***/ },
/* 26 */
/***/ function(module, exports) {
module.exports = require("url");
Why is this?
So it seems the issue was with me setting the target to electron but building for web. This seemed to work in webpack 1, but after updating to webpack 2 it no longer includes the runtime in both bundles.
The solution I took was to have two configs - one for electron (my main build), and one for web (specify the target in the webpack config)

Categories

Resources