asp.net react webpack 4 Hmr failing with 500 response - reactjs

After updating to webpack 4.19 Hmr stoped working and throws an error everythime I change something in a code.
project setup:
asp.net core 2.0
react
Web-pack
Recently I've updated from Webpack 2.5 to 4.19 as this kinda big update, I might be missing some configuration, after googling I came here to ask for help.
If I am missing anything, please just ask, I will provide any additional info.
Error I review after updating code:
Project folders/files structure
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const bundleOutputDir = './wwwroot/dist';
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
mode: isDevBuild ? "development" : "production",
entry: {
'main': './ClientApp/boot.tsx',
},
output: {
path: path.join(__dirname, bundleOutputDir),
filename: 'main.js',
publicPath: isDevBuild ? 'dist/' : ""
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
module: {
rules: [{
test: /\.tsx?$/,
include: /ClientApp/,
use: 'awesome-typescript-loader?silent=true'
},
{
test: /\.js$/,
include: /ClientApp/,
use: 'source-map-loader',
enforce: 'pre'
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff2|eot|woff|ttf)$/,
use: 'url-loader?limit=25000'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
]
},
optimization: isDevBuild ? {} : {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
uglifyOptions: {
compress: false,
ecma: 6,
mangle: true
},
sourceMap: true
})
]
},
plugins: [
new CheckerPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
})
].concat([
// Plugins that apply in development builds only
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
]),
externals: {
Config: JSON.stringify(isDevBuild ? require('./appsettings.Development.json') : require('./appsettings.json'))
}
}];
};
webpack.config.vendor.js
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
mode: isDevBuild ? "development" : "production",
stats: {
modules: false
},
resolve: {
extensions: ['.js']
},
module: {
rules: [{
test: /\.(png|jpg|jpeg|gif|svg|woff2|eot|woff|ttf)$/,
use: 'url-loader?limit=25000'
},
{
test: /\.css(\?|$)/,
use: [MiniCssExtractPlugin.loader, 'css-loader?minimize']
},
]
},
optimization: isDevBuild ? {} : {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
uglifyOptions: {
compress: false,
ecma: 6,
mangle: true
},
sourceMap: true
})
]
},
entry: {
vendor: ['bootstrap', 'bootstrap/dist/css/bootstrap.css', 'event-source-polyfill', 'isomorphic-fetch', 'react', 'react-dom', 'react-router-dom', 'jquery'],
},
output: {
path: path.join(__dirname, 'wwwroot', 'dist'),
publicPath: 'dist/',
filename: '[name].js',
library: '[name]_[hash]',
},
plugins: [
new MiniCssExtractPlugin({
filename: "vendor.css",
chunkFilename: "vendor.css"
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
// Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
new webpack.DllPlugin({
path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
name: '[name]_[hash]'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': isDevBuild ? '"development"' : '"production"'
})
]
}];
};
package.json
{
"name": "name",
"private": true,
"version": "0.0.0",
"devDependencies": {
"#types/enzyme": "^3.1.9",
"#types/history": "4.6.0",
"#types/jest": "^22.2.0",
"#types/react": "^16.4.14",
"#types/react-dom": "^16.0.7",
"#types/react-hot-loader": "^4.1.0",
"#types/react-router": "4.0.12",
"#types/react-router-dom": "4.0.5",
"#types/webpack-env": "^1.13.6",
"aspnet-webpack": "^3.0.0",
"aspnet-webpack-react": "^3.0.0",
"awesome-typescript-loader": "3.2.1",
"bootstrap": "3.3.7",
"css-loader": "^0.28.11",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5",
"event-source-polyfill": "0.0.9",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"isomorphic-fetch": "2.2.1",
"jest": "^22.4.2",
"jest-mock-axios": "^1.0.21",
"jquery": "3.2.1",
"json-loader": "^0.5.4",
"mini-css-extract-plugin": "^0.4.3",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-hot-loader": "^3.1.3",
"react-router-dom": "4.1.1",
"react-test-renderer": "^15.6.2",
"react-transition-group": "^2.2.1",
"style-loader": "^0.18.2",
"typescript": "^3.0.3",
"url-loader": "^0.5.9",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.0",
"webpack-dev-middleware": "^3.3.0",
"webpack-hot-middleware": "^2.24.1"
},
"dependencies": {
"#types/chart.js": "^2.7.25",
"#types/node": "^10.10.3",
"axios": "^0.17.1",
"babel-polyfill": "^6.26.0",
"chart.js": "^2.7.2",
"classnames": "^2.2.5",
"font-awesome": "^4.7.0",
"linq": "^3.1.0",
"moment": "^2.22.2",
"primeicons": "^1.0.0-beta.9",
"primereact": "^1.6.2",
"tslint": "^5.9.1",
"tslint-react": "^3.4.0",
"uglifyjs-webpack-plugin": "^2.0.1"
},
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"build": "./node_modules/.bin/webpack",
"tslint": "tslint -p tsconfig.json",
"debug": "node --debug-brk --inspect ./node_modules/jest/bin/jest -i"
},
"jest": {
"setupFiles": [
"<rootDir>/test-shim.js",
"<rootDir>/test-setup.js"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(ts|tsx)$": "<rootDir>/test-preprocessor.js"
},
"testMatch": [
"**/__tests__/*.(ts|tsx|js)"
]
}
}
entry class - boot.tsx
// tslint:disable-next-line:no-var-requires
require("react-hot-loader/patch");
import "./css/site.css";
import "bootstrap";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import "babel-polyfill";
import { BrowserRouter } from "react-router-dom";
import * as RoutesModule from "./routes";
import
{ AppConfig } from "./appconfig";
import axios, { AxiosRequestConfig } from "axios";
// Import PrimeReact required files and selected theme
import "primereact/resources/primereact.min.css";
import "font-awesome/css/font-awesome.css";
import "primeicons/primeicons.css";
import "primereact/resources/themes/ludvig/theme.css";
import "./css/custom.css";
let routes = RoutesModule.routes;
axios.defaults.headers.common.Authorization = "Basic " + window.btoa(AppConfig.testUser + ":" + AppConfig.testPassword);
function renderApp() {
// This code starts up the React app when it runs in a browser. It sets up the routing
// configuration and injects the app into a DOM element.
const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href")!;
ReactDOM.render(
<AppContainer>
<BrowserRouter children={routes} basename={baseUrl} />
</AppContainer>,
document.getElementById("react-app"),
);
}
renderApp();
// Allow Hot Module Replacement
if (module.hot) {
module.hot.accept("./routes", () => {
routes = require<typeof RoutesModule>("./routes").routes;
renderApp();
});
}
Startup.cs
namespace name
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
//ReactHotModuleReplacement = true,
//HotModuleReplacementEndpoint = "/__webpack_hmr"
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
}

Solved. !
By simply adding babel-loader with all additional dependencies and updating webpack.config.js with
{
test: /\.(j|t)sx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
cacheDirectory: true,
babelrc: false,
presets: [
[
"#babel/preset-env",
{
targets: {
browsers: "last 2 versions"
}
}
],
"#babel/preset-typescript",
"#babel/preset-react"
],
plugins: [
["#babel/plugin-proposal-decorators", {
legacy: true
}],
["#babel/plugin-proposal-class-properties", {
loose: true
}],
"react-hot-loader/babel"
]
}
}
},

Related

webpack5 + react + typescript deployed but getting blank screen

I am having difficulty with resolving webpack deployment issue.
In development mode everything seems fine, but as I deployed this project through vercel, I am only getting blank screen. When I checked the page, nothing but index.html existed. (no other js files)
These are my files below. I really want to know what on earth the problem is.
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('#pmmmwh/react-refresh-webpack-plugin');
const { ESBuildMinifyPlugin } = require('esbuild-loader');
const isDevelopment = process.env.NODE_ENV !== 'production';
module.exports = () => ({
mode: isDevelopment ? 'development' : 'production',
devtool: isDevelopment ? 'eval-cheap-module-source-map' : false,
cache: { type: isDevelopment ? 'memory' : 'filesystem' },
entry: './src/index.tsx',
target: 'web',
output: {
filename: 'js/[name]-[chunkhash].js',
assetModuleFilename: 'img/[hash][ext][query]',
pathinfo: false,
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
clean: true,
},
resolve: {
alias: {
icons: path.resolve(__dirname, 'public/assets/icons'),
images: path.resolve(__dirname, 'public/assets/images'),
components: path.resolve(__dirname, 'src/components'),
data: path.resolve(__dirname, 'src/data'),
docs: path.resolve(__dirname, 'src/docs'),
pages: path.resolve(__dirname, 'src/pages'),
styles: path.resolve(__dirname, 'src/styles'),
types: path.resolve(__dirname, 'src/types'),
utils: path.resolve(__dirname, 'src/utils'),
},
extensions: ['*', '.js', '.tsx', '.ts'],
modules: [__dirname, 'src', 'node_modules'],
fallback: {
process: require.resolve('process/browser'),
zlib: require.resolve('browserify-zlib'),
stream: require.resolve('stream-browserify'),
util: require.resolve('util'),
buffer: require.resolve('buffer'),
assert: require.resolve('assert'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'esnext',
},
},
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset/resource',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(ts|tsx)$/,
exclude: '/node_modules',
use: 'ts-loader',
},
{
test: /restructure(\/|\\)src(\/|\\)[\w]+.js/,
use: [
{
loader: 'imports-loader',
options: {
type: 'commonjs',
imports: 'multiple ../../buffer/ Buffer Buffer',
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
}),
new webpack.EnvironmentPlugin({
DEVELOPMENT: isDevelopment,
}),
isDevelopment && new webpack.HotModuleReplacementPlugin(),
isDevelopment && new ReactRefreshWebpackPlugin(),
],
optimization: {
minimize: !isDevelopment,
minimizer: [
new ESBuildMinifyPlugin({
target: 'esnext',
css: true,
}),
],
splitChunks: {
chunks: 'all',
},
},
externals: {
axios: 'axios',
},
devServer: {
port: 3000,
open: true,
hot: true,
historyApiFallback: true,
},
});
package.json
{
"name": "webpack",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"engines": {
"node": ">=16.15.0",
"yarn": ">=1.22.17"
},
"scripts": {
"build": "webpack",
"dev": "NODE_ENV=development & webpack serve --progress",
"build:dev": "NODE_ENV=development & yarn build",
"build:prod": "NODE_ENV=production & yarn build"
},
"keywords": [],
"devDependencies": {
"#pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"#react-pdf/types": "^2.1.0",
"#types/react": "^18.0.12",
"#types/react-dom": "^18.0.5",
"#types/shortid": "^0.0.29",
"#types/styled-components": "^5.1.25",
"#typescript-eslint/eslint-plugin": "^5.27.1",
"#typescript-eslint/parser": "^5.27.1",
"css-loader": "^6.7.1",
"esbuild-loader": "^2.19.0",
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"html-webpack-plugin": "^5.5.0",
"imports-loader": "^4.0.0",
"lodash": "^4.17.21",
"prettier": "^2.6.2",
"react-refresh": "^0.13.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.3.0",
"typescript": "^4.7.3",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.2"
},
"dependencies": {
"#react-pdf/renderer": "^2.2.0",
"assert": "^2.0.0",
"axios": "^0.27.2",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"node-env": "^0.1.6",
"process": "^0.11.10",
"react": "^18.1.0",
"react-daum-postcode": "^3.1.1",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"shortid": "^2.2.16",
"stream-browserify": "^3.0.0",
"styled-components": "^5.3.5",
"styled-reset": "^4.4.1",
"util": "^0.12.4"
}
}
index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import GlobalFont from 'styles/globalFont';
import GlobalStyle from 'styles/globalStyle';
import App from './App';
const rootEl = document.getElementById('root');
if (!rootEl) throw new Error('Failed to find the root element');
const root = createRoot(rootEl);
root.render(
<React.StrictMode>
<GlobalFont />
<GlobalStyle />
<App />
</React.StrictMode>,
);
App.tsx
import Home from 'pages/Home';
import PdfViewer from 'pages/PdfViewer';
import Test from 'pages/Test';
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/docs" element={<PdfViewer />} />
<Route path="/test" element={<Test />} />
</Routes>
</BrowserRouter>
);
}
export default App;

react-scripts build giving error - "MiniCssExtractPlugin is not a constructor" [duplicate]

[webpack-cli] TypeError: MiniCssExtractPlugin is not a constructor
at module.exports (/home/andrey/smartadmin-app/webpack.config.js:70:9)
at loadConfigByPath (/home/andrey/smartadmin-app/node_modules/webpack-cli/lib/webpack-cli.js:1745:27)
at async WebpackCLI.loadConfig (/home/andrey/smartadmin-app/node_modules/webpack-cli/lib/webpack-cli.js:1830:30)
at async WebpackCLI.createCompiler (/home/andrey/smartadmin-app/node_modules/webpack-cli/lib/webpack-cli.js:2185:18)
at async Command.<anonymous> (/home/andrey/smartadmin-app/node_modules/#webpack-cli/serve/lib/index.js:98:30)
at async Promise.all (index 1)
at async Command.<anonymous> (/home/andrey/smartadmin-app/node_modules/webpack-cli/lib/webpack-cli.js:1672:7)
I have got this error and don't understand why it happens. I didn't change any package version and have just done npm install, and I was broken.
{
"name": "react-typescript",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"view-file": "ts-node --project ts.node.config.json ./webpack/createViewFile.ts",
"build-dev": "webpack --env mode=development && npm run build-storybook",
"build-dev-server": "webpack --env mode=development",
"build-prod": "webpack --env mode=production",
"lint": "eslint 'client/**'",
"storybook": "start-storybook -p 5000",
"build-storybook": "build-storybook -o ./public/storybook",
"start": "webpack serve --open"
},
"dependencies": {
"axios": "0.24.0",
"clsx": "1.1.1",
"dotenv": "8.6.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-hook-form": "7.19.5",
"react-router-dom": "6.0.2",
"react-string-replace": "0.4.4",
"webpack-cli": "4.9.1"
},
"devDependencies": {
"#babel/core": "7.16.0",
"#storybook/addon-actions": "6.4.0-rc.2",
"#storybook/addon-docs": "6.4.0-rc.2",
"#storybook/addon-essentials": "6.4.0-rc.2",
"#storybook/addon-links": "6.4.0-rc.2",
"#storybook/builder-webpack5": "6.4.0-rc.2",
"#storybook/manager-webpack5": "6.4.0-rc.2",
"#storybook/preset-scss": "1.0.3",
"#storybook/react": "6.4.0-rc.2",
"#types/express": "4.17.13",
"#types/node": "16.11.7",
"#types/react": "17.0.33",
"#types/react-dom": "17.0.10",
"#typescript-eslint/eslint-plugin": "5.2.0",
"#typescript-eslint/parser": "5.2.0",
"babel-loader": "8.2.3",
"clean-webpack-plugin": "4.0.0",
"compression-webpack-plugin": "9.0.0",
"copy-webpack-plugin": "9.1.0",
"css-loader": "6.5.1",
"eslint": "7.32.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-import": "2.25.2",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-react": "7.26.1",
"eslint-plugin-react-hooks": "1.7.0",
"html-webpack-plugin": "5.5.0",
"mini-css-extract-plugin": "2.4.4",
"prettier": "2.4.1",
"sass": "1.43.4",
"sass-loader": "12.3.0",
"style-loader": "3.3.1",
"terser-webpack-plugin": "5.2.4",
"ts-loader": "9.2.6",
"ts-node": "10.4.0",
"typescript": "4.4.4",
"webpack": "5.64.1",
"webpack-dev-server": "4.7.3",
"webpack-manifest-plugin": "4.1.1",
"webpack-nano": "1.1.1"
}
}
The command which I execute is npm run start, and my webpack.config.js file is:
require('dotenv').config();
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const CopyPlugin = require('copy-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const { execSync } = require('child_process');
const rules = require('./webpack/rules');
const paths = require('./webpack/paths');
const enviroment = require('./webpack/env');
module.exports = (env) => {
const isProduction = env.mode === enviroment.PRODUCTION;
const isDevServer = !!env.WEBPACK_SERVE;
if (isDevServer) {
return {
watch: true,
mode: enviroment.DEVELOPMENT,
entry: './src/index.tsx',
stats: {
errorDetails: true,
},
module: {
rules: [rules.ts(), rules.css()],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: ['node_modules', 'src'],
},
output: {
filename: 'main.js',
path: paths.public,
publicPath: paths.public,
},
devServer: {
port: 8080,
host: 'localhost',
static: [paths.public],
compress: true,
hot: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
historyApiFallback: {
index: '/index.html',
},
client: {
overlay: true,
logging: 'info',
progress: true,
},
devMiddleware: {
writeToDisk: true,
},
},
plugins: [
{
apply(compiler) {
compiler.hooks.environment.tap('removePublicFolder', () => {
execSync('rm -rf public');
});
},
},
new CompressionPlugin(),
new MiniCssExtractPlugin({
filename: isDevServer ? 'main.css' : '[name].[hash].css',
ignoreOrder: true,
}),
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env),
}),
new WebpackManifestPlugin({
publicPath: '',
}),
new CopyPlugin({
patterns: [
{ from: paths.views, to: paths.public },
{ from: paths.static, to: paths.public },
],
}),
],
};
}
return {
mode: env.mode,
entry: './src/index.tsx',
stats: {
errorDetails: true,
},
...(isProduction && {
optimization: {
minimize: true,
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
},
}),
module: {
rules: [rules.ts(), rules.css()],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: ['node_modules', 'src'],
},
output: {
filename: '[name].[contenthash].js',
path: paths.public,
publicPath: '/',
},
plugins: [
{
apply(compiler) {
compiler.hooks.environment.tap('removePublicFolder', () => {
execSync('rm -rf public');
});
},
},
new CompressionPlugin(),
new MiniCssExtractPlugin({
filename: isDevServer ? 'main.css' : '[name].[hash].css',
ignoreOrder: true,
}),
new webpack.DefinePlugin({
'process.env': JSON.stringify(process.env),
}),
new WebpackManifestPlugin({
publicPath: '',
}),
{
apply(compiler) {
compiler.hooks.done.tap('done', () => {
execSync('npm run view-file');
execSync('ls');
});
},
},
new CopyPlugin({
patterns: [{ from: paths.static, to: paths.public }],
}),
],
};
};
Also I will attach ./webpack/rules.js, to make it clearer:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const paths = require('./paths');
const css = () => {
return {
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
{
loader: 'sass-loader',
options: {
sassOptions: {
indentWidth: 4,
includePaths: [paths.styles],
},
},
},
],
};
};
const ts = () => {
return {
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
configFile: 'tsconfig.json',
},
},
],
};
};
module.exports = {
ts,
css,
};
There is an open issue for this on the create-react-app repository. It looks like this comment has a temporary fix of adding the following to file package.json:
The below works if you're using Yarn:
"resolutions": {
"mini-css-extract-plugin": "2.4.5"
},
Use the command below for npm:
npm i -D --save-exact mini-css-extract-plugin#2.4.5
Update: As of January 17, it appears that this issue is fixed by mini-css-extract-plugin v2.5.1. You should be able to remove the version pin created above and npm i or yarn install to update.
For me it was working too and after an update it just stopped working, but as the error suggests, the constructor is undefined, so I changed the way the plugin was called and it worked.
Add '.default' in the call as below:
const MiniCssExtractPlugin = require("mini-css-extract-plugin").default;
As suggested by Macedo_Montalvão and Sharpek:
You can go to your React project folder.
Then head over to this path:
node_modules\react-scripts\config
Open the webpack.config.js file.
Replace
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
with
const MiniCssExtractPlugin = require('mini-css-extract-plugin').default;
This should work.
There are breaking changes in this plugin (I think this was by mistake). Here you can read more about this:
The best solution is to change import linked as Macedo_Montalvão said.
const MiniCssExtractPlugin = require("mini-css-extract-plugin").default;
This error was fixed in version 2.5.1.

Visual Studio React Typescript webpack-dev-server hot reload not working for component changes

I am using .Net Core 3.1 with React, Typescript and webpack for an SPA. I have multiple modules in react which I load on different page/view of Asp.Net core application.
I am facing issue with auto refresh or hot reload. When I change a parameter which I am passing from module to a component hot reload works as expected BUT if I do any change in component then those are not getting reflected in browser.
If I browse https://localhost:5010/webpack-dev-server I can see home.bundle.js there. Also if change value of "msg" param/prop of HomeComponent in HomeModule.tsx, I can see new bundle generated for home.bundle.js with fresh hashcode and change also reflects to browser on https://localhost:5010/home/ BUT If I do change to HomeComponent.tsx ex: if I change "Home component" to "Home component 123" this change not getting reflected neither on https://localhost:5010/home nor the new bundle NOT generating on https://localhost:5010/webpack-dev-server.
Here is my project structure and files including configuration files. Any help will be highly appreciated.
Update: I added HotModuleReplacementPlugin to webpack.dev.config.js and now when I modify something in component HomeComponent.tsx I see a new entry in https://localhost:5010/webpack-dev-server something like "5f80de77a97d48407bd9.hot-update.json". If I open this file it's content is like {"h":"0dce1f9e44bff4d47fb4","c":{}}
Apart from this another issue is when I run application with F5 from Visual Studio it takes couple of seconds to load website until than browser shows "This site can't be reached"
Project stucture
Solution
Solution
- AspNetCore.Web
- node_modules
- wwwroot
- js
- Controllers
- Views
- Scripts (React)
- components
- HomeComponent.tsx
- modules
- HomeModule.tsx
- package.json
- tsconfig.json
- webpack.config.js
- webpack.dev.js
- Other .Net Core projects
Package.json
{
"version": "1.0.0",
"name": "aspnetcore-react",
"private": true,
"scripts": {
"build": "webpack serve --config webpack.dev.config.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"axios": "^0.21.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router": "^5.2.0"
},
"devDependencies": {
"#babel/core": "^7.14.3",
"#babel/plugin-transform-runtime": "^7.14.3",
"#babel/preset-env": "^7.14.2",
"#babel/preset-react": "^7.13.13",
"#babel/preset-typescript": "^7.13.0",
"#babel/runtime": "^7.14.0",
"#popperjs/core": "^2.2.1",
"#types/react": "^16.9.35",
"#types/react-dom": "^16.9.8",
"#types/webpack": "^4.41.25",
"#types/webpack-dev-server": "^3.11.1",
"autoprefixer": "^9.7.6",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.2",
"bootstrap": "^4.4.1",
"clean-webpack-plugin": "^4.0.0-alpha.0",
"css-loader": "^3.5.1",
"file-loader": "^6.0.0",
"jquery": "^3.6.0",
"node-sass": "^4.14.1",
"postcss-loader": "^3.0.0",
"resolve-url-loader": "^3.1.3",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.3",
"ts-loader": "^7.0.2",
"ts-node": "^10.0.0",
"typescript": "3.8.3",
"url-loader": "^4.1.0",
"vue-loader": "^15.9.2",
"webpack": "^5.11.1",
"webpack-cli": "^4.3.1",
"webpack-dev-server": "^3.11.1",
"webpack-merge": "^5.7.3"
}
}
tsconfig.json
{
"compilerOptions": {
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
},
"exclude": [
"wwwroot/js",
"node_modules"
],
"include": [
"./Scripts/**/*"
]
}
webpack.dev.config.ts
import path from "path";
import webpack from "webpack";
import * as fs from 'fs';
const modulePath = './Scripts/modules/'
const entries = {
home: modulePath + "HomeModule.tsx",
editor: modulePath + "EditorModule.tsx"
};
const config: webpack.Configuration = {
mode: "development",
output: {
filename: "[name].bundle.js",
publicPath: "/js",
},
entry: entries,
module: {
rules: [
{
test: /\.(ts|js)x?$/i,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
},
},
},
{
test: /\.(scss)$/,
use: [
{
// Adds CSS to the DOM by injecting a `<style>` tag
loader: 'style-loader'
},
{
// Interprets `#import` and `url()` like `import/require()` and will resolve them
loader: 'css-loader'
},
{
loader: "resolve-url-loader"
},
{
// Loader for webpack to process CSS with PostCSS
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
},
{
// Loads a SASS/SCSS file and compiles it to CSS
loader: 'sass-loader'
}
]
},
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 4096,
fallback: {
loader: 'file-loader',
options: {
name: 'assets/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/[name].[hash:8].[ext]'
}
}
],
},
{
test: /\.html$/,
use: [{
loader: "html-loader",
options: {
minimize: true
}
}]
}
],
},
resolve: {
extensions: [".tsx", ".jsx", ".ts", ".js"],
},
plugins: [
],
devtool: "inline-source-map",
devServer: {
contentBase: path.join(__dirname, "build"),
historyApiFallback: true,
port: 5010,
open: false,
hot: true,
https: {
key: fs.readFileSync('./Scripts/generated/webpack_cert.key'),
cert: fs.readFileSync('./Scripts/generated/webpack_cert.crt'),
}
}
};
export default config;
HomeModule.tsx
import * as React from 'react';
import * as ReactDom from 'react-dom';
import HomeComponent from '../components/HomeComponent';
const homeRoot = document.getElementById('home');
ReactDom.render(<HomeComponent msg={'Hello World!!!'} />, homeRoot);
HomeComponent.tsx
import * as React from 'react';
export interface IHomeComponentProps {
msg: string
}
export interface IHomeComponentState {
}
export default class HomeComponent extends React.Component<IHomeComponentProps, IHomeComponentState> {
constructor(props: IHomeComponentProps) {
super(props);
this.state = {
}
}
public render() {
return (
<div>
<h2>Home component</h2>
<p><b>{this.props.msg}</b></p>
</div>
);
}
}
Views/Home/Index.cshtml
#{
ViewData["Title"] = "Home Page";
ViewBag.NoContainer = true;
}
<div class="home-header">
<h2 class="home-header-subtitle">Welcome to the</h2>
<h1 class="home-header-title">AspnetCoreReact</h1>
<div class="home-header-actions">
<a class="btn btn-primary home-header-button" asp-area="" asp-controller="Editor" asp-action="Index">Try the demo</a>
</div>
<div id="home">
</div>
</div>
<script src="~/js/home.bundle.js" type="module"></script>
Startup.cs
namespace AspnetCoreReact.Web
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication();
services.AddInfrastructure(Configuration, Environment);
services.AddScoped<ICurrentUserService, CurrentUserService>();
services.AddHttpContextAccessor();
services.AddHealthChecks()
.AddDbContextCheck<AspNetReactDbContext>();
ConfigureCookieSettings(services);
// Add memory cache services
services.AddMemoryCache(); // To do: Change to distributed cache later
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
services.AddMvc();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHealthChecks("/health");
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Scripts";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "build:hotdev");
}
});
}
}
}
launchSettings.json
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:53158",
"sslPort": 44329
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"AspnetCore-React": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}
I finally able to resolve the issue by replacing UseReactDevelopmentServer with UseProxyToSpaDevelopmentServer.
New code block (Working)
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Scripts";
if (env.IsDevelopment())
{
// Ensure that you start webpack server - npm run build
string webpackDevServer = "https://localhost:5010/";
spa.UseProxyToSpaDevelopmentServer(webpackDevServer);
}
});
Old code block (Not working)
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Scripts";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "build:hotdev");
}
});

webpack-hot-middleware doesn't generate hot-update.json file

I'm trying to get up and running my app using webpack-dev-middleware, webpack-hot-middle-ware, express and react. But when I startup my code, system doesn't give me [hash].hot-update.json file. When I change something in my code system rebuilds and give such file but with wrong hash in file name. Here is my code:
entry point for development server:
require('ignore-styles').default(undefined, (module, filename) => {
if ((['.png', '.jpg'].some(ext => filename.endsWith(ext)) )) {
module.exports = '/build/' + path.basename(filename);
}
});
require('babel-register')({
ignore: [ /(node_modules)/ ],
presets: ["env", "stage-0", "react"],
plugins: [
'dynamic-import-node',
'syntax-dynamic-import',
'react-loadable/babel'
]
});
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const Loadable = require('react-loadable');
const webpackDevMiddleware = require('webpack-dev-middleware');
const serverRenderer = require('./server').default;
const config = require('../configs/webpack.common.js');
const compiler = webpack(config);
const app = express();
app.use(webpackDevMiddleware(compiler, { serverSideRender: true, publicPath: '/build/', }));
app.use(require("webpack-hot-middleware")(compiler));
app.use(serverRenderer());
Loadable.preloadAll().then(() => {
app.listen(3000, () => console.log('Development server is running on port 3000'));
}).catch(err => {
console.log(err);
});
My webpack.config.js file:
module.exports = [{
name: 'client',
mode: 'development',
target: 'web',
entry: ['webpack-hot-middleware/client', './app/index.js'],
output: {
path: path.join(__dirname, '../build'),
filename: 'client.js',
publicPath: '/build/',
hotUpdateChunkFilename: '[id].[hash].hot-update.js',
hotUpdateMainFilename: '[hash].hot-update.json',
},
devtool: 'source-map',
plugins: [
new ManifestPlugin({
writeToFileEmit: true,
}),
new WriteFilePlugin(),
new webpack.HotModuleReplacementPlugin(),
],
module: {
rules: [
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(png|jpg|gif|ico|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
}
}
]
}
],
},
},
{
name: 'server',
mode: 'development',
target: 'node',
entry: './server/server.js',
externals: [nodeExternals()],
output: {
path: path.join(__dirname, '../build'),
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: '/build/',
},
plugins: [
new WriteFilePlugin(),
],
module: {
rules: [
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(png|jpg|gif|ico|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
emitFile: false
}
}
]
}
]
}
}];
My package.json file:
{
"name": "xxx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node server/development.js",
"build": "rimraf build && NODE_ENV=production webpack --config configs/webpack.production.js --progress --profile --colors",
"prod": "node server/production.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"css-loader": "^0.28.11",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"ignore-styles": "^5.0.1",
"mini-css-extract-plugin": "^0.4.0",
"node-sass": "^4.9.0",
"react-hot-loader": "^4.1.2",
"rimraf": "^2.6.2",
"sass-loader": "^7.0.1",
"serialize-javascript": "^1.5.0",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.5",
"webpack": "^4.6.0",
"webpack-cli": "^2.0.15",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.22.1",
"webpack-manifest-plugin": "^2.0.2",
"webpack-merge": "^4.1.2",
"webpack-node-externals": "^1.7.2",
"write-file-webpack-plugin": "^4.3.2"
},
"dependencies": {
"react": "^16.4.0",
"react-dom": "^16.4.0",
"react-helmet": "^5.2.0",
"react-loadable": "^5.3.1",
"react-redux": "^5.0.7",
"react-router-config": "^1.0.0-beta.4",
"react-router-dom": "^4.2.2",
"redux": "^4.0.0",
"redux-thunk": "^2.2.0"
}
}
and my server.js file:
import React from 'react'
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import { Provider } from 'react-redux'
import { Helmet } from 'react-helmet';
import Loadable from 'react-loadable';
import serialize from 'serialize-javascript';
import configureStore from '../app/store';
import routes from '../app/routes';
import template from './template';
const store = configureStore();
const normalizeAssets = (assets) => {
return Array.isArray(assets) ? assets : [assets]
};
const searchModuleFiles = (modules, assets) => {
return modules.map(module => {
let files = [`${module}.js`, `${module}.css`];
return files.map(file => ({ file: assets[file] }));
})
};
export default (stats = null) => (req, res) => {
const context = {};
const modules = [];
let devStats = [];
let mainScript = null;
const content = renderToString(
<Provider store={store}>
<StaticRouter context={context} location={req.url}>
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
{renderRoutes(routes)}
</Loadable.Capture>
</StaticRouter>
</Provider>
);
if(!stats) {
let chunks = res.locals.webpackStats.toJson().children.filter(item => item.name === 'client')[0].assetsByChunkName;
devStats = normalizeAssets(modules.map(module => chunks[module])[0]).map(item => ({ file: `/build/${item}` }));
mainScript = { file: '/build/client.js' };
} else {
mainScript = { file: stats['main.js'] };
}
let bundles = stats ? searchModuleFiles(modules, stats)[0] : devStats;
let scripts = bundles.filter(bundle => bundle.file.endsWith('.js'));
let styles = bundles.filter(bundle => bundle.file.endsWith('.css'));
const helmet = Helmet.renderStatic();
const data = serialize(store.getState());
res.status(200).send(template({ content, data, mainScript, scripts, styles, helmet }));
}
Also any help about server side rendering wanted.

Module build failed: syntaxerror: Unexpected token (React config)

I'm having some sort of issue getting React to play nice with web pack and render in this project. I'm getting a syntax unexpected token error.
console error
here is my main.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class MyComponentClass extends React.Component {
render() {
return 'Test Component';
}
}
ReactDOM.render( <MyComponentClass />, document.getElementById('root'));
here is my Webpack.config file:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const config = {
entry: {
app: './client/scripts/main.js',
},
output: {
filename: 'app.js',
path: path.join(__dirname, 'public')
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
include: __dirname + '/client/scripts',
loader: 'babel-loader',
},
{
test: /\.css$/,
loader: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
importLoaders: 1,
localIdentName: '[name]__[local]'
}
},
{
loader: 'postcss-loader',
},
],
})),
},
{
test: /\.html/,
loader: 'html-loader',
},
{
test: /\.(jpe?g|png|gif)$/i,
loader: 'file-loader',
options: {
hash: 'sha512',
digest: 'hex',
name: 'images/[name].[ext]',
},
},
],
},
plugins: [
new ExtractTextPlugin({
filename: 'app.css',
ignoreOrder: true,
}),
new webpack.HotModuleReplacementPlugin()
],
resolve: {
extensions: ['.css', '.js'],
modules: [
path.join(__dirname, 'src'),
'node_modules',
],
},
devServer: {
contentBase: path.join(__dirname, "public"),
hot: true,
port: 8005,
}
};
module.exports = config;
And my package.json
{
"name": "jumpcloud-ui-assignment",
"version": "1.0.0",
"description": "A project that can be used to asses front end developers",
"repository": {
"type": "git",
"url": "git://github.com/TheJumpCloud/jumpcloud-ui-assignment.git"
},
"private": true,
"author": "JumpCloud",
"bugs": {
"url": "https://github.com/TheJumpCloud/jumpcloud-ui-assignment/issues"
},
"scripts": {
"start:client": "webpack-dev-server --progress --inline",
"start:server": "node server/server.js"
},
"dependencies": {
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"body-parser": "^1.15.2",
"express": "^4.14.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"underscore": "^1.8.3"
},
"devDependencies": {
"autoprefixer": "^7.1.6",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"css-hot-loader": "^1.3.3",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"postcss-loader": "^2.0.8",
"script-loader": "^0.7.2",
"style-loader": "^0.19.0",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.4"
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}
Im sure it's a webpack configuration issue, after scowering the other similar issues here im coming up blank.
Add a '.babelrc' file to your project containing
{
"presets": [ "es2015", "react" ]
}
And it should be ok

Resources