Webpack build does not work with crawlers - javascript

My build works perfectly fine in browsers (even legacy v40 < chrome). It does not work with any crawler I have tried so far. There's the same error which strangely only happens in crawlers (such as googlebot) which makes it incredibly difficult to pinpoint the issue.
I tried
Disabling production build to see unminified errors, but crawlers then refuse to load the js file as it is too big
Running the site in as many browsers as I can (except IE) it works in all of them.
Disabling preloading for crawlers (renders the app's html using headless chrome)
This is the error that googlebot sees:
You can test this yourself at https://wavedistrict.com
Webpack config:
const { resolve } = require("path")
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin")
const WebpackPwaManifest = require("webpack-pwa-manifest")
const webpackMerge = require("webpack-merge")
const Visualizer = require("webpack-visualizer-plugin")
const isProduction = process.env.NODE_ENV === "production"
/**
* Variable for the project root.
* Change this when moving the configuration files
*/
const projectRoot = resolve(__dirname)
const sourceFolder = resolve(projectRoot, "src")
const tsFolder = resolve(sourceFolder, "ts")
const buildFolder = resolve(projectRoot, "build")
const publicFolder = resolve(projectRoot, "public")
const htmlTemplateFile = resolve(publicFolder, "index.html")
const tsconfigPath = resolve(projectRoot, "tsconfig.json")
const tslintPath = resolve(projectRoot, "tslint.json")
const tsLoader = {
loader: "ts-loader",
options: {
compilerOptions: {
module: "esnext",
target: "es5",
allowSyntheticDefaultImports: true,
},
transpileOnly: true,
configFile: tsconfigPath,
allowTsInNodeModules: true,
},
}
const babelLoader = {
loader: "babel-loader",
}
const workerRule = {
test: /\.worker\.ts$/,
use: {
loader: "worker-loader",
},
}
const babelRule = {
test: /\.(js|ts|tsx)$/,
use: [babelLoader],
}
const sassRule = {
test: /\.scss$/,
use: [
isProduction
? MiniCssExtractPlugin.loader
: {
loader: "style-loader",
options: {
singleton: true,
},
},
{ loader: "css-loader" },
{
loader: "sass-loader",
options: {
data: "#import './ts/modules/core/styles/_.scss';",
includePaths: [sourceFolder],
},
},
],
}
/** #type {import('webpack').Configuration} */
const baseConfig = {
context: projectRoot,
entry: [
"babel-polyfill",
"url-search-params-polyfill",
resolve(tsFolder, "init"),
],
output: {
filename: "js/[name].js",
path: buildFolder,
publicPath: "/",
},
module: {
rules: [workerRule, babelRule, sassRule],
},
resolve: {
modules: ["node_modules"],
extensions: [".js", ".ts", ".tsx", ".scss"],
alias: {
modules: resolve(tsFolder, "modules"),
common: resolve(tsFolder, "common"),
},
mainFields: ["jsnext:main", "module", "main"],
},
plugins: [
new CopyWebpackPlugin([
{
from: publicFolder,
ignore: [htmlTemplateFile],
},
]),
new CleanWebpackPlugin(buildFolder, { root: projectRoot, verbose: false }),
/**new ForkTsCheckerWebpackPlugin({
tslint: tslintPath,
tsconfig: tsconfigPath,
}),**/
],
stats: {
children: false,
entrypoints: false,
modules: false,
},
}
if (process.argv.includes("--stats")) {
if (baseConfig.plugins) {
baseConfig.plugins.push(new Visualizer())
}
}
const devConfig = webpackMerge(baseConfig, {
mode: "development",
plugins: [
new HtmlWebpackPlugin({
template: htmlTemplateFile,
chunksSortMode: "dependency",
}),
],
devtool: "inline-source-map",
devServer: {
hot: false,
historyApiFallback: true,
},
})
const prodConfig = webpackMerge(baseConfig, {
mode: "production",
optimization: {
minimize: true,
nodeEnv: "production",
},
plugins: [
new WebpackPwaManifest({
name: "WaveDistrict",
short_name: "WaveDistrict",
description: "",
background_color: "#091F35",
theme_color: "#00ba8c",
orientation: "any",
icons: [
{
src: resolve(publicFolder, "img/logo.svg"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
{
src: resolve(publicFolder, "img/logo.png"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
],
}),
new MiniCssExtractPlugin({
filename: "css/[name].css",
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: htmlTemplateFile,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
new HtmlWebpackInlineSourcePlugin(),
],
performance: {
maxAssetSize: 500000,
},
devtool: "source-map",
})
module.exports = isProduction ? prodConfig : devConfig
Babel config (needed to convert ES6 node_modules to ES5):
const babelEnv = {
targets: {
chrome: "41", // For googlebot
},
}
/** Keep track of all conflicting dependencies here */
const nonES5Deps = ["qs", "querystring", "query-string", "decko"]
module.exports = function(api) {
api.cache(true)
return {
exclude: [],
include: (path) => {
if (nonES5Deps.some((p) => path.match(p))) {
return true
}
if (path.match(/node_modules/)) return false
return true
},
presets: [
["#babel/preset-env", babelEnv],
"#babel/preset-react",
"#babel/preset-typescript",
],
plugins: [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-transform-arrow-functions",
[
"#babel/plugin-proposal-decorators",
{
legacy: true,
},
],
[
"#babel/plugin-proposal-class-properties",
{
loose: true,
},
],
],
}
}
So what's going on here? How can I debug the issue when it only appears in crawlers?

I have discovered the issue with the help of a friend. It appears Googlebot (and other crawlers) does not support the AudioContext object, hence undefined is not a function.
Modifying my code to check for it and safely disabling functionality if it doesn't exist has solved the problem.

Related

webpack-dev-server and WASM HMR

I'm trying to make a real time shooter game on the web using ThreeJS and Rapier. Rapier is a physics engine written in rust and can be used in JS using the WASM module. Now in development webpack-dev-server and HMR are really a god given tool, if it works. The RAPIER WASM module gets loaded but from the wrong port. My HTTP server runs on http://localhost:8080 but the devserver on -:9000. The fetch mechanism that webpack uses to get the WASM doesn't configure the port for the fetch. Is this a bug or misconfiguration on my end?
My webpack.config.js:
const dotenv = require('dotenv');
dotenv.config();
const path = require('path');
const zlib = require('zlib');
const CompressionPlugin = require('compression-webpack-plugin');
const ThreadsPlugin = require('threads-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackBar = require('webpackbar');
const { WebpackManifestPlugin: ManifestPlugin } = require('webpack-manifest-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const DEBUG = process.env.NODE_ENV !== 'production';
const FILENAME = DEBUG ? '[name]' : '[name].[contenthash]';
const CHUNK_FILENAME = DEBUG ? '[name]' : '[id].[contenthash]';
module.exports = {
mode: DEBUG ? 'development' : 'production',
entry: { bundle: path.join(__dirname, 'resources/js/bundle.js') },
output: {
path: path.join(__dirname, 'public'),
filename: FILENAME + '.js',
chunkFilename: CHUNK_FILENAME + '.js',
publicPath: '',
clean: true,
},
module: {
rules: [
{
test: '/\.m?js$/',
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{ test: /\.(frag|vert)$/, use: 'raw-loader' },
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 2 } },
'postcss-loader',
'sass-loader',
]
},
{ test: /\.(png|svg|jpg|jpeg|webm|gif)$/i, type: 'asset/resource' },
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
{ test: /\.svelte$/, use: { loader: 'svelte-loader', options: {
emitCss: false,
hotReload: DEBUG,
compilerOptions: { dev: DEBUG },
hotOptions: {
preserveLocalState: false,
noReload: false, // prevent reload after fatal error
acceptAccesssors: true,
acceptNamedExports: true
}
} } },
{ test: /node_modules\/svelte\/.*\.mjs$/, resolve: { fullySpecified: false } }
]
},
plugins: [
new CopyPlugin({ patterns: [
// { from: './resources/images', to: 'images' },
{ from: './resources/textures', to: 'textures' },
{ from: './resources/scenes', to: 'scenes' },
] }),
new ManifestPlugin(),
new MiniCssExtractPlugin({
filename: FILENAME + '.css',
chunkFilename: CHUNK_FILENAME + '.css'
}),
// new ThreadsPlugin(),
new WebpackBar(),
].concat(DEBUG ? [] : [
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
compressionOptions: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
},
},
threshold: 10240,
minRatio: 0.8,
})
]),
optimization: {
minimize: true,
minimizer: [ new TerserPlugin(), new CssMinimizerPlugin() ],
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
devServer: {
port: 9000,
static: path.join(__dirname, 'public'),
hot: true,
client: {
overlay: {
errors: true,
warnings: false,
},
progress: true,
reconnect: 10,
webSocketURL: {
hostname: 'localhost',
port: 9000,
}
},
compress: true
},
devtool: DEBUG ? 'inline-source-map' : false,
experiments: {
asyncWebAssembly: true
}
};
I've googled a fair bit for solutions but so far I haven't found anything that looks like my particular issue.

WARNING in Unknown plugin: imageminSvgo. Did you forget to install the plugin?

This is the Warning that I receive from Webpack, despite installing the imageminSvgo plugin.
I have used it within the Image Minimizer Plugin as imageminSvgo, but Webpack doesn't seem to detect it.
I would really appreciate some help in knowing how to use this plugin in my project in the best way.
Here are my webpack.config.js configs.
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
// const imageminSvgo = require('imagemin-svgo')
const mode = "production";
module.exports = {
devtool: false,
stats: "errors-warnings",
resolve: {
alias: {
CssFolder: path.resolve(__dirname, "src/stylesheets/"),
JsFolder: path.resolve(__dirname, "src/javascripts/"),
ImgFolder: path.resolve(__dirname, "src/images/"),
},
},
entry: { TOAA: "./src/index.js" },
mode: mode,
devServer: {
port: 8080,
// contentBase: "./build",
hot: true,
// overlay: true,
compress: true,
historyApiFallback: true,
},
output: {
filename: "[name]-[contenthash].bundle.js",
path: path.resolve(__dirname, "build"),
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
plugins: [
// imageminSvgo({
// plugins: [
// {
// name: "removeViewBox",
// active: false,
// },
// ],
// }),
new CompressionPlugin(),
new HtmlWebpackPlugin(
{ template: "./public/index.html" },
{ inject: true }
),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:
mode === "production"
? "[name]-[contenthash].bundle.css"
: "[name].css",
}),
new ImageMinimizerPlugin({
minimizerOptions: {
// Lossless optimization with custom option
// Feel free to experiment with options for better result for you
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
["imageminSvgo", { removeViewBox: true }],
// Svgo configuration here https://github.com/svg/svgo#configuration
[
"svgo",
{
plugins: [
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
{
name: "preset-default",
params: {
overrides: {
// customize plugin options
convertShapeToPath: {
convertArcs: true,
},
// disable plugins
convertPathData: false,
},
},
},
],
},
],
],
},
}),
new NodePolyfillPlugin(),
],
module: {
rules: [
{
test: /\.css$/i,
use: [
{ loader: MiniCssExtractPlugin.loader, options: {} },
{ loader: "css-loader", options: { importLoaders: 1 } },
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"autoprefixer",
{
overrideBrowserslist: ["last 3 versions", "ie >9"],
},
],
],
},
},
},
],
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.(jpe?g|png|gif|svg|jpg)$/i,
type: "asset",
},
{
test: /\.html$/i,
loader: "html-loader",
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
};
Please let me know if additional info is required.
Try reinstall imagemin forcing the installation of the plugins. Use something like this: npm install -g imagemin-cli#3.0.0 --unsafe-perm=true --allow-root

core-js is large in bundle (using 62kB)

I'm reducing my js bundle size and stumbled upon core-js. It takes around 62kB which represents ~24% of the whole package.
I tried using #babel/preset-env, but wasn't able to shrink the size any further. Not sure if I'm using the "right" settings:
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
The full webpack.config.js
const path = require('path');
const webpack = require('webpack');
const dotenv = require('dotenv');
const copyWebpackPlugin = require('copy-webpack-plugin');
const bundleOutputDir = './dist';
/* eslint-disable import/no-extraneous-dependencies */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// const CompressionPlugin = require('compression-webpack-plugin');
module.exports = (env) => {
// get project ID (environment)
const projectID = process.env.PROJECT_ID;
if (!projectID || projectID === undefined) {
throw new Error('Need env variable PROJECT_ID');
}
const isDevEnvironment = !(projectID === 'production-project');
const isDevBuild = !(env && env.prod);
const analyzeBundle = env && env.analyze;
// call dotenv and it will return an Object with a parsed key
const dotEnv = isDevEnvironment ? dotenv.config({ path: './.env.development' }).parsed : dotenv.config().parsed;
// reduce it to a nice object, the same as before
const envKeys = Object.keys(dotEnv).reduce((prev, next) => {
const updatedPrev = prev;
updatedPrev[`process.env.${next}`] = JSON.stringify(dotEnv[next]);
return updatedPrev;
}, {});
envKeys['process.env.PROJECT_ID'] = JSON.stringify(projectID);
// need to remove quotes from env
const publicURL = 'https:/mysite.com'
const plugins = [new webpack.DefinePlugin(envKeys), new ForkTsCheckerWebpackPlugin()];
if (isDevBuild) {
// eslint-disable-next-line new-cap
plugins.push(new webpack.SourceMapDevToolPlugin(), new copyWebpackPlugin([{ from: 'dev/' }]));
} else {
// Don't need to enable compressinon plugin as Firebase Hosting automatically zips the files for us
// plugins.push(new CompressionPlugin());
}
if (analyzeBundle) {
// eslint-disable-next-line global-require
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins.push(new BundleAnalyzerPlugin());
}
const babelPlugins = [
// syntax sugar found in React components
'#babel/proposal-class-properties',
'#babel/proposal-object-rest-spread',
// transpile JSX/TSX to JS
[
'#babel/plugin-transform-react-jsx',
{
// we use Preact, which has `Preact.h` instead of `React.createElement`
pragma: 'h',
pragmaFrag: 'Fragment',
},
],
[
'transform-react-remove-prop-types',
{
removeImport: !isDevBuild,
},
],
];
if (!isDevBuild) {
babelPlugins.push(['transform-remove-console', { exclude: ['error', 'warn'] }]);
}
return [
{
entry: './src/index.ts',
output: {
filename: 'widget.js',
path: path.resolve(bundleOutputDir),
publicPath: isDevBuild ? '' : publicURL,
},
devServer: {
host: '0.0.0.0', // your ip address
port: 8080,
disableHostCheck: true,
contentBase: bundleOutputDir,
open: 'google chrome',
},
plugins,
optimization: {
minimize: !isDevBuild,
nodeEnv: 'production',
mangleWasmImports: true,
removeAvailableModules: true,
usedExports: true,
sideEffects: true,
providedExports: true,
concatenateModules: true,
},
mode: isDevBuild ? 'development' : 'production',
module: {
rules: [
// packs PNG's discovered in url() into bundle
{
test: /\.(jpe?g|png|webp)$/i,
use: [
{
loader: 'responsive-loader',
options: {
// eslint-disable-next-line global-require
adapter: require('responsive-loader/sharp'),
// sizes: [160, 320, 640, 960, 1280],
name: '[path][name]-[width].[ext]',
sourceMap: isDevBuild,
},
},
],
},
{ test: /\.svg/, use: ['#svgr/webpack'] },
{
test: /\.(css)$/i,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
},
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
],
},
{
test: /\.(scss)$/i,
use: [
{
loader: 'style-loader',
options: { injectType: 'singletonStyleTag' },
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: isDevBuild,
},
},
],
},
// use babel-loader for TS and JS modeles,
// starting v7 Babel babel-loader can transpile TS into JS,
// so no need for ts-loader
// note, that in dev we still use tsc for type checking
{
test: /\.(js|ts|tsx|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
],
[
// enable transpiling ts => js
'#babel/typescript',
// tell babel to compile JSX using into Preact
{ jsxPragma: 'h' },
],
],
plugins: babelPlugins,
},
},
],
},
],
},
resolve: {
extensions: ['*', '.js', '.ts', '.tsx'],
plugins: [new TsconfigPathsPlugin()],
alias: {
react: 'preact/compat',
'react-dom': 'preact/compat',
images: path.join(__dirname, 'images'),
sharedimages: path.resolve(__dirname, '../../packages/shared/src/resources'),
},
},
},
];
};
It looks like the targets property for #babel/preset-env is no longer used and instead the browserlist is recommended to include the list of supported browsers.
https://babeljs.io/docs/en/babel-preset-env#browserslist-integration

css module not apply to react component after Webpack build

I'm currently working with the latest version of Webpack to build files from TSX and SCSS files, so my issue is after building files to JS and CSS modules. all of the styles don't apply to any react components,
you can see my configure below
This is my SCSS file.
// Home.module.scss
.addgame {
background-color: aqua;
}
My React file
import React from 'react';
const styles = require('./Home.module.scss');
export default function HomePage() {
return (
<div className={styles.addgame}>HomePage</div>
);
}
I have 2 files of Webpack
//Webpack.config.js
// const webpack = require('webpack');
const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
plugins: [
new HtmlWebpackPlugin({
hash: true,
template: './src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css',
}),
],
optimization: {
moduleIds: 'deterministic',
runtimeChunk: true,
}
});
this is common Webpack
//webpack.common.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
context: process.cwd(),
entry: {
index: './src/index.tsx',
},
plugins: [new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }), new WebpackManifestPlugin()],
module: {
rules: [
{
test: /\.ts(x?)$/,
use: ['babel-loader', 'ts-loader'],
exclude: /node_modules/,
},
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: /\.s[ac]ss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
modules: {
namedExport: true,
},
},
},
{
loader: 'css-loader',
options: {
modules: true,
// Run `postcss-loader` on each CSS `#import`, do not forget that `sass-loader` compile non CSS `#import`'s into a single file
// If you need run `sass-loader` and `postcss-loader` on each CSS `#import` please set it to `2`
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
{
// asset
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
// asset
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', 'scss'],
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, '../build'),
pathinfo: false,
publicPath: '/',
},
};
//postcss configure
module.exports = {
plugins: [
"postcss-preset-env",
"postcss-modules"
]
}
///babel
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
["#babel/preset-typescript"],
["#babel/preset-flow"],
["#babel/preset-react"]
],
"plugins": [
["#babel/plugin-transform-flow-strip-types"],
["const-enum"],
["#babel/plugin-transform-typescript"],
["#babel/plugin-syntax-dynamic-import"],
["#loadable/babel-plugin"]
]
}

Refresh or typing URL gives 404 error on webpack production build in React.js. How to fix it?

I'm using React Router v4 and Webpack for build project.
When I build my application in production mode and refresh the page I'm getting 404 error. "The request path could not been found"
But when I use "yarn start", I did not get a 404 error.
I saw that added the following sentence to index.html as the solution:
<script src='/static/js/bundle.js'></script>
But I can't find "bundle.js" file in my project or browser's local file.
App.js
import React, {Component} from 'react';
import './App.css';
import {Route, Switch} from "react-router-dom";
import Homefrom "./page/Home";
import About from "./page/About";
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Switch>
<Route exact path="/home" component={Home}/>
<Route exact path="/about" component={About}/>
</Switch>
);
}
}
export default withRouter(App);
Root.js
const Root = () => {
return (
<Router history={history}>
<Route path="/" component={App}/>
</Router>
);
};
export default Root;
index.js
import React from 'react';
import ReactDOM from 'react-dom'
import 'core-js';
import 'react-app-polyfill/ie11';
import './index.css';
import * as serviceWorker from './serviceWorker';
import Root from "./Root";
ReactDOM.render(<Root/>, document.getElementById('root'));
serviceWorker.unregister();
webpack.config.prod.js
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const publicPath = paths.servedPath;
const shouldUseRelativeAssetPaths = publicPath === './';
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const publicUrl = publicPath.slice(0, -1);
const env = getClientEnvironment(publicUrl);
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
}
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
});
}
return loaders;
};
module.exports = {
mode: 'production',
bail: true,
results.
devtool: shouldUseSourceMap ? 'source-map' : false,
entry: [paths.appIndexJs],
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: shouldUseSourceMap,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
inline: false,
annotation: true,
}
: false,
},
}),
],
splitChunks: {
chunks: 'all',
name: false,
},
runtimeChunk: true,
},
resolve: {
modules: ['node_modules'].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
'react-native': 'react-native-web',
},
plugins: [
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
cacheCompression: true,
compact: true,
},
},
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: true,
sourceMaps: false,
},
},
{
test: cssRegex,
exclude: cssModuleRegex,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: shouldUseSourceMap,
}),
sideEffects: true,
},
{
test: cssModuleRegex,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
{
test: sassRegex,
exclude: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
},
'sass-loader'
),
sideEffects: true,
},
{
test: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
{
loader: require.resolve('file-loader'),
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
hash: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
new ModuleNotFoundPlugin(paths.appPath),
new webpack.DefinePlugin(env.stringified),
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
new RegExp('^/_'),
new RegExp('/[^/]+\\.[^/]+$'),
],
}),
// TypeScript type checking
fs.existsSync(paths.appTsConfig) &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrc,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
localhost:5000's folder
--static
--js
--1.841adf67.chunck.js?296dbda6ec241c2c7629
--App.js
--Root.js
--history.js
--index.js
--main.83c8fdd1.chunck.js?296dbda6ec241c2c7629
--runtime~main.229c360f.js?296dbda6ec241c2c7629
Thanks for reading.

Categories