Webpack 5, Server-Side rendering, and FOUC - reactjs

I'm upgrading an existing web site from Webpack 3 to Webpack 5.
The site uses server side rendering for the first load, the client side routing for any in-site navigation. All this worked fine with Webpack 3, but after migrating to Webpack 5 it looks like some of the styles are being applied via javascript and that's creating a FOUC during the first load (after that, in-site navigation works fine and looks correct). As a test, I turned off javascript in my browser; the old site loads fine and looks correct, but the upgraded site does not. It feels like I need style-loader in the server config somewhere, but when that's added, I get a "Cannot GET /" when trying to load the site. Any help is appreciated.
Server-side config
require('dotenv').config({ silent: true });
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const includePaths = [path.resolve(__dirname, 'stylesheets')];
module.exports = {
bail: true,
entry: {
main: './src/entry-server',
},
output: {
path: path.join(__dirname, 'build', 'prerender'),
filename: '[name].js',
publicPath: '/bundle/',
libraryTarget: 'commonjs2',
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
PRERENDER: true,
ASSETS_CDN_PREFIX: JSON.stringify(process.env.ASSETS_CDN_PREFIX || ''),
},
}),
// only load moment english locale: https://github.com/moment/moment/issues/2517
new webpack.ContextReplacementPlugin(/moment[\\/]locale$/, /^\.\/(en)$/),
new webpack.optimize.ModuleConcatenationPlugin(),
new MiniCssExtractPlugin({
ignoreOrder: true,
}),
],
module: {
rules: [
{
test: /\.jsx?$/,
include: path.join(__dirname, 'src'),
use: 'babel-loader',
},
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
'postcss-loader',
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths,
data: `$assetprefix: "${process.env.ASSETS_CDN_PREFIX || ''}";`,
},
},
},
],
},
{
test: /\.svg$/,
use: `svg-inline-loader?removeTags&removingTags[]=${['defs', 'style', 'title'].join(',removingTags[]=')}`,
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.css', '.scss', '.json'],
},
target: 'node',
};
Server entry point
export default function (req, res, environmentConstants, callback) {
// ...setup
match({ routes, location: targetUrl }, (error, redirectLocation, renderProps) => {
// ...setup
fetchSomeData().then(() => renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>,
))
.then((content) => {
callback(null, {
helmet: Helmet.renderStatic(),
content,
initialState: serialize(store.getState(), { isJSON: true }),
env: serialize(someEnvConstants),
});
})
Client-side config
require('dotenv').config({ silent: true });
const AssetsPlugin = require('assets-webpack-plugin');
const CleanPlugin = require('clean-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const webpack = require('webpack');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const includePaths = [path.resolve(__dirname, 'stylesheets')];
// Match all routes that we want to lazy load
const lazyRouteRegex = /route\/([^/]+\/?[^/]+)Route.jsx$/;
module.exports = {
bail: true,
entry: {
main: './src/entry-client',
vendor: [
'react',
'react-dom',
'react-router',
'redux',
'react-redux',
'xmldom',
],
},
output: {
path: path.join(__dirname, 'build', 'public', '[fullhash]'),
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: `${process.env.ASSETS_CDN_PREFIX || ''}/build/public/[fullhash]/`,
},
plugins: [
// only load moment english locale: https://github.com/moment/moment/issues/2517
new webpack.ContextReplacementPlugin(/moment[\\/]locale$/, /^\.\/(en)$/),
new MiniCssExtractPlugin({
ignoreOrder: true,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
PRERENDER: false,
ASSETS_CDN_PREFIX: JSON.stringify(process.env.ASSETS_CDN_PREFIX || ''),
},
}),
new AssetsPlugin(),
new CleanPlugin([path.join(__dirname, 'build', 'public')]),
new CompressionPlugin(),
// new BundleAnalyzerPlugin(),
],
module: {
rules: [
{
test: /\.jsx?$/,
include: path.join(__dirname, 'src'),
exclude: lazyRouteRegex,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: lazyRouteRegex,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: 'bundle-loader',
options: {
lazy: true,
},
},
{
loader: 'babel-loader',
},
],
},
{
test: /swiper.*\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: false,
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths,
data: `$assetprefix: "${process.env.ASSETS_CDN_PREFIX || ''}";`,
},
},
},
],
},
{
test: /\.s?css$/,
exclude: /swiper.*\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
{
loader: 'postcss-loader',
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths,
data: `$assetprefix: "${process.env.ASSETS_CDN_PREFIX || ''}";`,
},
},
},
],
},
{
test: /\.svg$/,
use: `svg-inline-loader?removeTags&removingTags[]=${['defs', 'style', 'title'].join(',removingTags[]=')}`,
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.css', '.scss', '.json'],
},
target: 'web',
};

Related

IE explore compatible issue with Yeoman generate Office add-in

I used Yeoman for generating my Office add-in.
And got stuck with an IE11-compatible issue. here is it:
enter image description here
I check the taskpane.js file find it still using deconstruct expression which is from ES6.
enter image description here
after that, I checked the webpack.config.js file. I google it, so the babel-loader should be used to convert ES6 to ES5.
here is the file:
/* eslint-disable no-undef */
const devCerts = require("office-addin-dev-certs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const urlDev = "https://localhost:3000/";
const urlProd = "xxxxxxx"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION
async function getHttpsOptions() {
const httpsOptions = await devCerts.getHttpsServerOptions();
return { cacert: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert };
}
module.exports = async (env, options) => {
const dev = options.mode === "development";
const config = {
devtool: "source-map",
entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
vendor: ["react", "react-dom", "core-js", "#fluentui/react"],
taskpane: ["react-hot-loader/patch", "./src/taskpane/index.js"],
commands: "./src/commands/commands.js",
},
output: {
clean: true,
},
resolve: {
extensions: [".ts", ".tsx", ".html", ".js"],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
"react-hot-loader/webpack",
{
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
],
exclude: /node_modules/,
},
{
test: /\.html$/,
exclude: /node_modules/,
use: "html-loader",
},
{
test: /\.(png|jpg|jpeg|gif|ico|svg)$/,
type: "asset/resource",
generator: {
filename: "assets/[name][ext][query]",
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]" + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
}
},
},
],
}),
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["taskpane", "vendor", "polyfill"],
}),
new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["commands"],
}),
new webpack.ProvidePlugin({
Promise: ["es6-promise", "Promise"],
}),
new webpack.ProvidePlugin({
"React": "react",
}),
],
devServer: {
hot: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
https: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
port: process.env.npm_package_config_dev_server_port || 3000,
},
};
return config;
};
I found it works in my code but not the library.
before:
enter image description here
after:
enter image description here
I have tried to add one more rule in config file, like:
{
test: /\.(ts|tsx)$/,
use: [
"react-hot-loader/webpack",
{
loader: "babel-loader",
options: {
presets: ["#babel/preset-typescript"],
}
},
],
exclude: /node_modules/,
},

In react, Websocket connection failed error in react after installed webpack and deploy to DigitalOcean

Here is my webpack config:
webpack.common.js
const { ContextReplacementPlugin, IgnorePlugin } = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
const path = require('path');
const babelOptions = {
babelrc: true,
extends: path.join(__dirname, '/.babelrc'),
cacheDirectory: true,
};
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
assetModuleFilename: '[path][contenthash][ext]',
},
stats: {
all: false,
modules: true,
errors: true,
warnings: true,
moduleTrace: true,
errorDetails: true,
performance: true,
reasons: true,
assets: true,
assetsSort: 'size',
usedExports: true,
},
resolve: {
extensions: [
'.webpack.js',
'.web.js',
'.mjs',
'.js',
'.jsx',
'.json',
'.ts',
'.tsx',
],
alias: {
// assets: path.resolve(__dirname, 'src/assets'),
react: path.resolve('./node_modules/react'),
},
},
module: {
rules: [
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
{
test: /\.mp4$/,
use: 'file-loader?name=videos/[name].[ext]',
},
{
test: /\.(jpe?g|png|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
// Inline files smaller than 10 kB (10240 bytes)
maxSize: 10 * 1024,
},
},
use: [
{
loader: 'image-webpack-loader',
options: {
name: '[path][contenthash].[ext]',
},
},
],
},
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'ts-loader',
options: { transpileOnly: true },
},
],
},
{
test: /\.jsx?$/,
exclude: /node_modules\/(?!(kdbush|supercluster)\/).*/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
],
},
{
test: /\.js$/,
use: 'source-map-loader',
enforce: 'pre',
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),
// favicon: './public/favicon.ico',
// filename: 'index.html',
// manifest: './public/manifest.json',
}),
new ContextReplacementPlugin(/\/ethers\//, (data) => {
delete data.dependencies[0].critical;
return data;
}),
new NodePolyfillPlugin({
excludeAliases: ['console'],
}),
new ForkTsCheckerWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, 'public/favicon.ico'),
to: path.resolve(__dirname, 'build/favicon.ico'),
},
{
from: path.resolve(__dirname, 'public/manifest.json'),
to: path.resolve(__dirname, 'build/manifest.json'),
},
],
}),
],
ignoreWarnings: [/Failed to parse source map/],
devServer: {
historyApiFallback: true,
client: {
webSocketURL: 'ws://0.0.0.0:8080/ws',
},
},
};
webpack.prod.js
const CompressionPlugin = require('compression-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { merge } = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const ClosurePlugin = require('closure-webpack-plugin');
const webpack = require('webpack');
const Dotenv = require('dotenv-webpack');
const common = require('./webpack.common');
require('dotenv').config();
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
output: { filename: 'js/[name].[contenthash].js' },
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'resolve-url-loader',
'sass-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'styles/[name].[contenthash].css',
chunkFilename: 'styles/[id].[contenthash].css',
}),
new Dotenv({ systemvars: true }),
new CompressionPlugin({
filename: '[path][base].gz[query]',
test: /\.(js|css)$/,
algorithm: 'gzip',
compressionOptions: {
level: 9,
},
}),
new CompressionPlugin({
filename: '[path][base].br[query]',
test: /\.(js|css)$/,
algorithm: 'brotliCompress',
compressionOptions: {
level: 11,
},
}),
],
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const nodeModulePath = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/
);
if (nodeModulePath) {
const packageName = nodeModulePath[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
}
},
},
},
},
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin(),
new ClosurePlugin({ mode: 'STANDARD' }, {}),
],
},
devServer: { allowedHosts: 'all' },
});
The error is
Error Message in chrome devTool
This project is working in my local. Not working after deployment to digitalOcean.
I think the websocket port has the problem.
To fix this, how can I config webpack for production?
Please let me know your thought.
Thanks!

Webpack collecting wrong src path

I think that the whole problem is in configuring the webpack, the images are successfully collected in the folder when building, but when importing ...
Somewhere I found a solution indicating the public path, but somehow it did not grow together
webpack.config.js
const path = require('path');
// const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: { main: './src/lib/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: "umd",
library: "#compassplus/ui-mobicash"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: true,
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
],
include: /\.module\.css$/,
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
exclude: /\.module\.css$/,
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[hash:12].[ext]',
outputPath: 'images/',
esModule: false,
},
},
],
},
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'index.css',
}),
// new HtmlWebpackPlugin({
// template: './public/index.html',
// }),
// new webpack.ProvidePlugin({
// "React": "react",
// }),
],
externals: {
react: 'react',
},
resolve: {
extensions: ['.js', '.jsx'],
},
}
Путь указанный в src / Path in the src:
src="images/809853c38dec.svg"
In the React component, I hook it up via import and pass it as an object
import imgLight from './img/theme-light.svg';
<img src={img} alt='Картинка' className={style.img}></img>
Solved the problem using url-loader:
{
loader: "url-loader",
options: {
limit: 8192,
name: "static/media/[name].[hash:8].[ext]"
},
}

Webpack - "css-loader/locals" miss matched for server.build and client.build

I setup a webpack config to build server.build and client.build, but they both producing different css/locals.
webpack.server.js
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const serverConfig = {
name: 'server',
target: 'node',
externals: [nodeExternals()],
entry: [
__dirname+'/index.js'
],
output: {
path: path.join(__dirname, '/.build'),
filename: 'server.build.js',
publicPath: '.build/',
libraryTarget: 'commonjs2'
},
module: {
loaders: [
{
test:/\.(?:js|jsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.(?:css|less)$/,
use: "css-loader/locals?import=1&modules=1&localIdentName=[name]__[local]__[hash:base64:7]",
exclude: /\.(eot|woff|woff2|ttf|svg)(\?[\s\S]+)?$/
},
{
test: /\.(eot|woff|woff2|ttf|svg)(\?[\s\S]+)?$/,
loader: 'url-loader?limit=1000&name=./fonts/[name].[ext]?[hash:base64:5]#icomoon',
}
]
},
resolve: {
extensions: ['.jsx', '.js', '.json','.less']
}
};
module.exports = serverConfig;
webpack.client.js
const webpack = require('webpack');
const path = require('path');
const context = path.resolve(__dirname, 'src');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const config = {
name:"client",
context,
entry: __dirname+'/src/app.js',
output: {
path: __dirname+"/.build/assets",
filename: 'bundle.js'
},
devtool: "source-map",
module: {
rules: [
{
test:/\.(?:js|jsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
{
test: /\.(?:css|less)$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader?module',
options: {
sourceMap: true,
importLoader: true,
localIdentName:'[name]__[local]__[hash:base64:7]'
}
},
{
loader: 'less-loader',
options: {
sourceMap: true,
importLoader: true,
}
}
],
fallback: 'style-loader',
}),
exclude: /\.(eot|woff|woff2|ttf|svg)(\?[\s\S]+)?$/
},
{
test: /\.(eot|woff|woff2|ttf|svg)(\?[\s\S]+)?$/,
loader: 'url-loader?limit=1000&name=./fonts/[name].[ext]?[hash:base64:5]#icomoon',
}
]
},
plugins: [
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true,
}),
new webpack.DefinePlugin({
"process.env": {
BROWSER: JSON.stringify(true),
NODE_ENV: JSON.stringify("production")
}
}),
],
resolve: {
extensions: ['.jsx', '.js', '.json']
}
};
module.exports = config;
Ex: server.build.js -> style__cont__2KTI-wF
bundle.js/bundle.css (client) -> style__cont__58B3ts_
I am really stuck at this point, i am new to react and there is no perfect approach to import css server side.

Code Splitting ~ ReferenceError: System is not defined

My code hit the error
ReferenceError: System is not defined
In line
<IndexRoute getComponent={(nextState, cb) => System.import('./containers/Home').then(module => cb(null, module))} />
How do I define System. Is it related to my webpack settings? Here how I put it
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var stylus = require('stylus');
module.exports = {
debug: true,
devtool: 'eval',
entry: {
"vendor": ["react", "react-router", 'react-dom'],
"app": ['webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server', './app/App.js']
},
output: {
pathinfo: true,
path: path.resolve(__dirname, 'public'),
filename: '[name].js',
publicPath: 'http://localhost:3000/'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Welcome!',
template: './index_template.ejs',
inject: 'body'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false
},
compress: {
warnings: false
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity,
filename: 'vendor.bundle.js'
})
],
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loaders: ['react-hot', 'babel']
},
{
test: /\.styl$/,
exclude: /(node_modules|bower_components)/,
// loader: 'style!css?resolve url?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!postcss!stylus-loader'
loader: 'style!css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]!postcss!stylus-loader'
},
{
test: /\.(png|jpg)$/,
exclude: /(node_modules|bower_components)/,
loader: 'url-loader?name=images/[name].[ext]&limit=8192'
},
{
test: /\.(ttf|otf|eot)$/,
exclude: /(node_modules|bower_components)/,
loader: 'url-loader?name=fonts/[name].[ext]&limit=8192'
},
{
test: /\.css$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
},
{
test: /\.scss$/,
loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?resolve url'
},
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.js$/,
include: path.resolve('node_modules/mapbox-gl-shaders/index.js'),
loader: 'transform/cacheable?brfs'
}
]
},
resolve: {
root: path.join(__dirname, '..', 'app'),
alias: {
'react': path.join(__dirname, 'node_modules', 'react')
},
extensions: ['', '.js', '.jsx', '.json', '.css', '.styl', '.png', '.jpg', '.jpeg', '.gif']
},
stylus: {
'resolve url': true
},
postcss: function () {
return [autoprefixer];
}
};
(Disclaimer: I am new to React-Router and webpack, so this may be
obvious to someone with more familiarity)
Your webpack build stack probably doesn't have configured System.import. I suppose that you use webpack 1 and support of System.import will be only in webpack 2.
You can change your existing code to current popular code splitting solution:
<IndexRoute
getComponent={(nextState, cb) => {
require.ensure([], require => {
// default import is not supported in CommonJS standard
cb(null, require('./containers/Home').default)
});
}}
/>
Note that you must specify .default if you want to use default import in CommonJS standard.

Resources