React 18 Webpack 5 New Jest Testing with Server Side Rendering
We are using Webpack 5.89 and related package.
https://github.com/harshal1982/react18-ssr
Basic Project Structure
Package.json
{
"name": "harshal-ssr",
"version": "2.0.1",
"description": "Server side rendering project",
"main": "index.js",
"scripts": {
"clean:build": "rimraf build public",
"dev": "npm run clean:build && webpack --mode development --watch",
"test": "jest --coverage -u"
},
"author": "",
"license": "ISC",
"jest": {
"transform": {
"^.+\\.(js|ts)$": "babel-jest"
},
"transformIgnorePatterns": [],
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less|scss)$": "<rootDir>/tests/css/__mocks__/styleMock.js"
}
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"@babel/preset-typescript": "^7.23.2",
"@babel/register": "^7.22.15",
"@reduxjs/toolkit": "^1.9.7",
"async_hooks": "^1.0.0",
"axios": "^1.6.0",
"babel-loader": "^9.1.3",
"compression": "^1.7.4",
"concurrently": "^8.2.2",
"copy-webpack-plugin": "^11.0.0",
"express": "^4.18.2",
"express-http-proxy": "^2.0.0",
"fs": "^0.0.1-security",
"lodash": "^4.17.21",
"net": "^1.0.2",
"nodemon": "^3.0.1",
"nodemon-webpack-plugin": "^4.8.2",
"npm-run-all": "^4.1.5",
"path": "^0.12.7",
"progress-bar-webpack-plugin": "^2.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-redux": "^8.1.3",
"react-router-config": "^5.1.1",
"react-router-dom": "^6.18.0",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"rimraf": "^5.0.5",
"serialize-javascript": "^6.0.1",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.10.0",
"webpack-node-externals": "^3.0.0"
},
"devDependencies": {
"@babel/core": "^7.23.2",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-transform-runtime": "^7.23.2",
"@babel/preset-env": "^7.23.3",
"@babel/preset-react": "^7.23.3",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.1.2",
"@testing-library/react-hooks": "^8.0.1",
"babel-jest": "^29.7.0",
"css-loader": "^6.8.1",
"html-webpack-plugin": "^5.5.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"mini-css-extract-plugin": "^2.7.6",
"node-polyfill-webpack-plugin": "^2.0.1",
"nyc": "^15.1.0",
"react-test-renderer": "^18.2.0",
"redux-mock-store": "^1.5.4",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"style-loader": "^3.3.3",
"webpack-manifest-plugin": "^5.0.0"
}
}
webpack.config.js
module.exports = require(`./config/webpack.${process.env.NODE_ENV || 'development'}.js`);
.babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [ "@babel/plugin-transform-runtime","@babel/plugin-proposal-class-properties"]
}
config/webpack.client.common.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const config = {
module: {
rules: [
{
test: /\.?js|jsx$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.(svg|jpg|jpeg|png|gif|pdf)$/,
exclude: /fonts/,
use: [
{
loader: 'file-loader',
options: {
name: './images/[name].[ext]',
publicPath: '/',
},
},
],
},
{
test: /\.(scss|css)$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
},
};
module.exports = config;
config/webpack.development.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const WebpackManifestPlugin = require('webpack-manifest-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const NodemonPlugin = require('nodemon-webpack-plugin');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const clientcommonconfig = require('./webpack.client.common');
const servercommonconfig = require('./webpack.server.common')
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const clientConfig = merge(clientcommonconfig, {
mode: 'development',
target: 'web',
entry: './src/app/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../public'),
},
plugins: [
new ProgressBarPlugin(),
new CopyWebpackPlugin({
patterns: [
{from: "src/images", to: "images/"}
],
}),
new webpack.DefinePlugin({
HTTP_DEFAULT_TIMEOUT: 5000,
}),
],
});
const serverConfig = merge(servercommonconfig, {
mode: 'development',
target: 'node',
entry: './src/server/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../build'),
},
externals: [nodeExternals()],
plugins: [
new ProgressBarPlugin(),
new NodePolyfillPlugin(),
new NodemonPlugin({
watch: path.resolve('../build'),
ext: 'js,json',
filename: 'server.js',
verbose: true,
env: {
NODE_ENV: 'development',
},
}),
new webpack.DefinePlugin({
HTTP_DEFAULT_TIMEOUT: 5000,
}),
],
});
module.exports = [clientConfig, serverConfig];
config/webpack.server.common.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const config = {
module: {
rules: [
{
test: /\.?js|jsx$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test:/\.(scss|css)$/,
exclude: /node_modules/,
use: ['style-loader','css-loader', 'sass-loader'],
},
],
},
};
module.exports = config;
Labels: React Server Side Rendering