I am at the start of building my app so I have very little code at this point. When webpack generates the index.html file, it includes all of the Javascript twice which causes errors in React.
What could be wrong with my webpack config to make it duplicate all the javascript?
The html produced by HtmlWebpackPlugin contains:
<body>
<div id="app">
<div class="app-spinner"></div>
</div>
<script src="index.e089a4bb51043ee42dd1.js" type="text/javascript"></script>
<script src="vendors.25e5b2c570c03aac7cee.js" type="text/javascript"></script>
<script src="app.b2527e4e46c3c3b95a11.js" type="text/javascript"></script>
<script src="react.a6f26e32b775aaa7d17d.js" type="text/javascript"></script>
<script type="text/javascript" src="index.e089a4bb51043ee42dd1.js"></script><script type="text/javascript" src="vendors.25e5b2c570c03aac7cee.js"></script><script type="text/javascript" src="app.b2527e4e46c3c3b95a11.js"></script><script type="text/javascript" src="react.a6f26e32b775aaa7d17d.js"></script></body>
My webpack.config.js contains
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, { mode }) => {
var config = {
entry: {
app: './src/app.js',
react: ['react', 'react-dom'],
},
devtool: 'none',
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
hot: true,
port: 9000,
},
resolve: {
modules: ['src', 'node_modules'],
extensions: ['*', '.js', '.jsx', '.css', '.scss', '.sass'],
alias: {
'~config': path.resolve(__dirname, 'src/config'),
'~components': path.resolve(__dirname, 'src/components'),
'~styles': path.resolve(__dirname, 'src/styles'),
'~images': path.resolve(__dirname, 'src/images'),
'@material-ui/styles': path.resolve(__dirname, 'node_modules', '@material-ui/styles'),
},
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.[hash].js',
chunkFilename: '[name].[contenthash].js',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
// stats: {
// maxModules: Infinity,
// optimizationBailout: true,
// },
plugins: [
new HtmlWebpackPlugin({
minify: false,
template: require('html-webpack-template'),
inject: true,
title: 'Octopedia',
favicon: './public/images/favicon/favicon.ico',
appMountId: 'app',
meta: [{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }],
links: [
{ href: '/site.webmanifest', rel: 'manifest' },
{ href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap', rel: 'stylesheet' },
],
appMountHtmlSnippet: '<div class="app-spinner"></div>',
headHtmlSnippet:
'<style>div.app-spinner{position:fixed;top:50%;left:50%;border:16px solid #f3f3f3;border-top:16px solid #3498db;border-radius:50%;width:120px;height:120px;margin:-60px 0 0 -60px;z-index:1;animation:spin 2s linear infinite;}@keyframes spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}</style >',
}),
new CopyWebpackPlugin({
patterns: [{ from: 'public' }],
}),
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
presets: [['@babel/preset-env', { modules: false }], '@babel/preset-react'],
},
},
},
{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader: 'file-loader',
},
},
],
},
};
if (mode === 'development') {
config.mode = 'development';
config.devtool = 'eval-source-map';
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.module.rules.push({
test: /\.(sass|scss|css)/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
});
} else {
config.mode = 'production';
config.devtool = 'none';
config.plugins.push(new MiniCssExtractPlugin({ filename: 'index.css' }));
config.module.rules.push({
test: /\.(sass|scss|css)/,
exclude: /node_modules/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
});
}
return config;
};