How CSS files will load dynamically using react-loadable library on client side?
I have included react-loadable library on both server and client side rendering, from server-side rendering everything works fine but client side, how CSS will load dynamically?
webpack.config.prod.js : Client/Server -
'use strict';
const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const { ReactLoadablePlugin } = require('react-loadable/webpack');
const publicPath = paths.servedPath;
const shouldUseRelativeAssetPaths = publicPath === './';
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== '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 cssFilename = 'static/css/[name].[contenthash:8].css';
const client = {
bail: true,
devtool: shouldUseSourceMap ? 'source-map' : false,
entry: [require.resolve('./polyfills'), paths.appIndexJs],
output: {
// The build folder.
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath,
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
resolve: {
modules: ['node_modules', paths.appNodeModules].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)),
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
'react-native': 'react-native-web',
},
plugins: [
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
strictExportPresence: true,
rules: [
{
test: /\.(js|jsx|mjs)$/,
enforce: 'pre',
use: [
{
options: {
formatter: 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]',
},
},
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
compact: true,
plugins: ['react-loadable/babel'],
},
},
{
test: /\.(?:css|less)$/,
use: ExtractCssChunks.extract({
use: [
{
loader: 'css-loader?modules',
options: {
minimize: true,
sourceMap: shouldUseSourceMap,
importLoaders: true,
localIdentName: '[name]__[local]__[hash:base64:7]',
},
},
{
loader: 'less-loader',
options: {
minimize: true,
sourceMap: shouldUseSourceMap,
importLoaders: true,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
],
flexbox: 'no-2009',
}),
],
},
},
],
fallback: 'style-loader',
}),
exclude: /\.(eot|woff|woff2|ttf|otf|svg)(\?[\s\S]+)?$/,
},
{
loader: require.resolve('file-loader'),
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [
new InterpolateHtmlPlugin(env.raw),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: false,
minifyCSS: true,
minifyURLs: true,
},
}),
new webpack.DefinePlugin(env.stringified),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
comparisons: false,
},
mangle: {
safari10: true,
},
output: {
comments: false,
ascii_only: true,
},
sourceMap: shouldUseSourceMap,
}),
new ExtractCssChunks({
filename: cssFilename,
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest.js',
minChunks: Infinity,
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
new ReactLoadablePlugin({
filename: './build/react-loadable.json',
}),
new SWPrecacheWebpackPlugin({
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
return;
}
if (message.indexOf('Skipping static resource') === 0) {
return;
}
console.log(message);
},
minify: true,
navigateFallback: `${publicUrl}/index.html`,
navigateFallbackWhitelist: [/^(?!\/__).*/],
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
};
// Server render
const nodeExternals = require('webpack-node-externals');
const server = Object.assign({}, client);
server.target = 'node';
server.node = {
__filename: true,
__dirname: true,
};
server.externals = [nodeExternals()];
server.entry = [
'./server/middleware/renderer.js',
];
delete server.devtool;
delete server.node;
server.module = {};
server.plugins = [
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
];
server.output = {
path: paths.appBuild,
filename: 'handleRender.js',
publicPath,
libraryTarget: 'commonjs2',
};
server.module.rules = [{
test: /\.(?:js|jsx)$/,
exclude: /node_modules/,
loader: require.resolve('babel-loader'),
options: {
compact: true,
plugins: ['react-loadable/babel'],
},
},
{
test: /\.(?:css|less)$/,
loader: 'css-loader/locals?modules&localIdentName=[name]__[local]__[hash:base64:7]!less-loader',
exclude: /\.(eot|woff|woff2|ttf|otf|svg)(\?[\s\S]+)?$/,
}];
module.exports = [server, client];
Server index.js:
...
import Loadable from 'react-loadable';
import serverRenderer from '../build/handleRender.js';
...
router.use('*', serverRenderer);
...
app.use(router);
// Pre-load all compoenents
Loadable.preloadAll().then(() => {
app.listen(PORT, (error) => {
if (error) {
return console.log('something bad happened', error);
}
console.log(`listening on ${PORT}...`);
});
}).catch((e) => {
console.log('Loadable Error : ', e);
});
renderer.js:
import { renderToStringWithData } from 'react-apollo';
import Loadable from 'react-loadable';
import { getBundles } from 'react-loadable/webpack';
...
const mainApp = renderToStringWithData(<Loadable.Capture
report={moduleName => modules.push(moduleName)}
>
<App req={req} context={context} client={client} />
</Loadable.Capture>);
...
const bundles = getBundles(JSON.parse(stats), modules);
const styles = bundles.filter(bundle => bundle.file.endsWith('.css'));
const scripts = bundles.filter(bundle => bundle.file.endsWith('.js'));
...
//mainApp=>html
const replacedStyle = html.replace(
'<link id="codeSplittingStyle">',
styles.map(bundle => `<link
rel="stylesheet"
href="/${bundle.file}"/>`).join('\n'),
);
const replacedScript = replacedStyle.replace(
'<script id="codeSplittingScript"></script>',
scripts.map(bundle => `<script
type="text/javascript"
src="/${bundle.file}"></script>`).join('\n'),
);
...
return res.send(replacedScript);
Browser.js:
import React from 'react';
import ReactDOM from 'react-dom';
import Loadable from 'react-loadable';
import Browser from './layout/browser';
import registerServiceWorker from './registerServiceWorker';
Loadable.preloadReady().then(() => {
ReactDOM.hydrate(<Browser />, document.getElementById('root'));
});
registerServiceWorker();
Please have a look at "Desired output" section in extract-css-chunks-webpack-plugin repo (https://github.com/faceyspacey/extract-css-chunks-webpack-plugin#desired-output). It states that:
webpack-flush-chunks will scoop up the exact stylesheets to embed in your response. It essentially automates producing the above.
So you need to use webpack-flush-chunks in order to generate cssHash which is essential part of dynamic css loading as it determines when css chunks should be loaded.
Related
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!
I'd like to render my RemoteApp into my ShellApp. My RemoteApp has got a useEffect which makes an API call, but it seems is executed a lot of times.
So I suppose my remote component is re rendered more than it should be.
These are my webpack.config for ShellApp and RemoteApp:
Remote app
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const deps = require('./package.json').dependencies;
module.exports = {
output: {
publicPath: 'http://localhost:3003/',
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json', '*'],
},
devServer: {
port: 3003,
historyApiFallback: true,
hot: true,
},
module: {
rules: [
{
test: /\.m?js/,
type: 'javascript/auto',
resolve: {
fullySpecified: false,
},
},
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
{
test: /\.json$/,
loader: 'json-loader',
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
remotes: {},
exposes: {
'./Dashboard': './src/pages/Dashboard/Dashboard.jsx',
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
},
}),
new HtmlWebPackPlugin({
template: './public/index.html',
}),
new webpack.HotModuleReplacementPlugin(),
new ESLintPlugin({
extensions: ['js', 'jsx'],
}),
],
};
Shell app
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const deps = require('./package.json').dependencies;
module.exports = {
output: {
publicPath: 'http://localhost:3000/',
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json', '*'],
},
devServer: {
port: 3000,
historyApiFallback: true,
hot: true,
},
module: {
rules: [
{
test: /\.m?js/,
type: 'javascript/auto',
resolve: {
fullySpecified: false,
},
},
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.svg$/,
use: ['#svgr/webpack'],
},
{
test: /\.json$/,
loader: 'json-loader',
},
],
},
plugins: [
new ModuleFederationPlugin({
name: 'frontend_shell',
filename: 'remoteEntry.js',
remotes: {
dashboard: 'frontend_equity_credit#http://localhost:3003/remoteEntry.js',
},
exposes: {},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
},
}),
new HtmlWebPackPlugin({
template: './public/index.html',
}),
new webpack.HotModuleReplacementPlugin(),
new ESLintPlugin({
extensions: ['js', 'jsx'],
}),
],
};
I'm importing the remote component like this in Shell App.jsx
import Dashboard from 'dashboard/Dashboard';
function App() {
return (
<div>
<Dashboard/>
</div>
);
}
Finally, this is my Dashboard.jsx component inside the Remote application
import React, { useEffect } from 'react';
import { useStore } from './store/store';
function Dashboard() {
const { globalStore, actions, dispatch } = useStore();
const loggedUserId = globalStore.auth?.userInfo?.id;
useEffect(() => {
if (loggedUserId) {
dispatch(actions.getUsersPreferences(loggedUserId)); //API CALL
}
}, [loggedUserId]);
return (
<>
{/*render api response*/}
</>
);
}
export default Dashboard;
I got this in console:
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Any ideas?
I am working on performance optimization in react site last few days and I have read code-splitting documents using webpack. I have small knowledge about webpack bundling and code splitting. my webpack code.
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // eslint-disable-line prefer-destructuring
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const DuplicatePackageCheckerPlugin = require("duplicate-package-checker-webpack-plugin");
const CompressionPlugin = require('compression-webpack-plugin'); //gzip
const BrotliPlugin = require('brotli-webpack-plugin'); //brotli
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = require('config');
const webpackConfig = {
mode: config.get('webpack.mode'),
entry: path.resolve(__dirname, 'src/index.jsx'),
output: {
path: path.resolve(__dirname, config.get('webpack.output.path')),
filename: `${config.get('webpack.output.scripts')}/[name].[hash:8].js`,
chunkFilename: `${config.get('webpack.output.scripts')}/[name].[chunkhash:8].js`,
publicPath: config.get('webpack.publicPath'),
},
resolve: {
extensions: ['.js', '.jsx', '.css'],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: [
{
loader: 'babel-loader',
},
],
exclude: /node_modules/,
},
{ // config for sass compilation
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
{
loader: "sass-loader"
}
]
},
{ // config for images
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'images',
}
}
],
},
{ // config for fonts
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'fonts',
}
}
],
},
],
},
plugins: [
(config.get('webpack.mode') === 'production') ? new CleanWebpackPlugin([
config.get('webpack.output.path'),
]) : () => {},
(config.get('webpack.mode') === 'production') ? new DuplicatePackageCheckerPlugin() : () => {},
(config.get('webpack.mode') === 'production') ?(
new CompressionPlugin({ //gzip plugin
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css|html|svg|txt|eot|otf|ttf|gif|png|jpg)$/,
threshold: 8192,
minRatio: 0.8
}),
new BrotliPlugin({ //brotli plugin
asset: '[path].br[query]',
test: /\.(js|css|html|svg|txt|eot|otf|ttf|gif|png|jpg)$/, //(js|css|html|svg|txt|eot|otf|ttf|gif)$/
threshold: 10240,
minRatio: 0.6
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'public' }
]
})
):()=>{},
new HTMLWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),
}),
new MiniCssExtractPlugin({
filename: `${config.get('webpack.output.styles')}/[name].[chunkhash:8].css`,
chunkFilename: `${config.get('webpack.output.styles')}/[name].[chunkhash:8].css`,
}),
new CaseSensitivePathsPlugin(),
new BundleAnalyzerPlugin({
analyzerMode: process.env.BA_MODE ? process.env.BA_MODE : 'disabled',
}),
],
optimization: {
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
minSize: 0,
name: config.get('webpack.vendorsCodeSplitting') ? (module) => {
const re = /[\\/]node_modules[\\/](.*?)([\\/]|$)/;
const packageName = module.context.match(re)[1];
return `vendors/pkg.${packageName.replace('#', '')}`;
} : 'vendors',
priority: -10,
},
default: {
name: 'default',
minChunks: 2,
reuseExistingChunk: true,
enforce: true,
priority: -20,
},
},
},
minimizer: [
(config.get('webpack.uglify')) ? new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: !!config.get('webpack.sourcemap'),
}) : () => {},
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: [
'default', {
discardComments: {
removeAll: true,
},
},
],
},
}),
],
},
devtool: config.get('webpack.sourcemap'),
devServer: {
contentBase: path.resolve(__dirname, 'public'),
historyApiFallback: true,
publicPath: config.get('webpack.publicPath'),
open: config.get('webpack.open'),
overlay: true,
},
};
module.exports = webpackConfig;
I don't know what I am doing wrong with webpack configuration to improve Google page insight speed index. I also attached a screenshot bundle analysis. many third-party libraries use on a home page its impacts on page speed? thank in advanced
Im trying to start my style guide server but it keeps throwing following error:
I believe this occurs when the babel loaders are not configured for jsx files. But this isnt true as I am able to start my project without errors. But when I try to start the style guide I end up with this.
Here is my styleguide config file
module.exports = {
title: 'Component Library',
webpackConfig: Object.assign(
{},
require("./config/webpack/webpack.dev.config.js"),
{
/* Custom config options if required */
}
),
components: "source/components/**/*.jsx",
template: {
head: {
links: [
{
rel: "stylesheet",
href:
"https://fonts.googleapis.com/css?family=Poppins:400,400i,600,600i,700,700i&display=swap"
}
]
}
},
theme: {
fontFamily: {
base: '"Poppins", sans-serif'
}
},
styles: function styles(theme) {
return {
Playground: {
preview: {
backgroundColor: '#29292e'
},
},
Code: {
code: {
fontSize: 14,
},
},
};
},
};
Here is my webpack config
var webpack = require('webpack');
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// const InterpolateHtmlPlugin = require('interpolate-html-plugin');
const WebpackAssetsManifest = require('webpack-manifest-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const reactLoadablePlugin = require('react-loadable/webpack')
.ReactLoadablePlugin;
const workboxPlugin = require('workbox-webpack-plugin');
module.exports = (env) => ({
mode: 'development',
entry: path.join(__dirname, '../../index.js'),
output: {
filename: '[name].bundle.[hash].js',
chunkFilename: '[name].bundle.[hash].js',
path: path.join(__dirname, '../../../build'),
publicPath: '/'
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
// cacheGroupKey here is `commons` as the key of the cacheGroup
name(module, chunks, cacheGroupKey) {
const moduleFileName = module
.identifier()
.split('/')
.reduceRight(item => item);
const allChunksNames = chunks.map(item => item.name).join('~');
return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
},
chunks: 'all'
}
}
}
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: path.resolve(__dirname, 'node_modules'),
loader: 'babel-loader'
},
{
test: /\.svg(\?.*)?$/, // match img.svg and img.svg?param=value
use: [
'url-loader', // or file-loader or svg-url-loader
'svg-transform-loader'
]
},
{
test: /\.png(\?.*)?$/, // match img.svg and img.svg?param=value
use: [
'url-loader', // or file-loader or svg-url-loader
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
},
{
test: /\.(sa|sc|c)ss$/,
exclude: path.resolve(__dirname, 'node_modules'),
use: [
'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
},
resolve: {
extensions: ['.js', '.jsx']
},
plugins: [
new webpack.EnvironmentPlugin({
APP_ENVIRONMENT: process.env.APP_ENVIRONMENT,
API_KEY: process.env.API_KEY,
AUTH_DOMAIN: process.AUTH_DOMAIN,
DB_URL: process.env.DB_URL,
PROJECT_ID: process.env.PROJECT_ID
}),
new CleanWebpackPlugin({
path: path.join(__dirname, '../../../build')
}),
new WebpackAssetsManifest({
fileName: 'asset-manifest.json'
}),
new HtmlWebpackPlugin({
title: '<<app>>',
template: 'main.html',
minify: {
collapseWhitespace: false,
removeComments: true,
useShortDoctype: false
}
}),
new MiniCssExtractPlugin({
filename: 'style.[contenthash].css'
}),
new CopyPlugin([
{
from: 'public',
to: 'public'
}
]),
new ProgressBarPlugin({
format: ' build [:bar] ' + ':percent' + ' (:elapsed seconds)' + ' :msg'
}),
new reactLoadablePlugin({
filename: './react-loadable.json'
}),
new workboxPlugin.InjectManifest({
swSrc: path.join(__dirname, '../../public/service-worker.js')
})
],
devServer: {
contentBase: path.join(__dirname, '/'),
filename: 'main.html',
compress: true,
port: 3000,
historyApiFallback: true,
disableHostCheck: true,
useLocalIp: true,
host: '0.0.0.0'
},
devtool: 'eval-source-map'
});
The problem was with the webpack file I was exporting the webpack configurations as a function.
Earlier:
module.exports = (env) => ({
....your webpack configurations
})
Instead of exporting everything as a function I exported it as
Now:
module.exports = {
....your webpack configurations
}
But can someone tell me why the earlier implementation didnt work?
I am trying to integrate react js application into existing AEM CMS application and below is the way how i am trying to put the values in setting for AEM CMS
And below is the configuration generated by CMS in div :
<div data-t-name="CustomComponent"
data-css="xxx/styles/main.css"
data-js="xxx/main.js">
<div
id="xxxx"
data-application-name="xxx" data-config='
{
"ID": "bb70cfde887ba7157a2dec3ff1d539ea40d817d97ad270278ba982008f918f96",
"EMBEDDED": true,
"LOCALE": "en",
"organizationId": "xxx",
"eventId": "xxx"
}'>
</div>
</div>
Issue is when i try to put css and js referance as entry point into above setting below is error i have received , any one please guide or clues to sort this issue
Please find the webpack config below :
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const project = require('../project.config');
const inProject = path.resolve.bind(path, project.basePath);
const inProjectSrc = (file) => inProject(project.srcDir, file);
const __DEV__ = project.env === 'development';
const __TEST__ = project.env === 'test';
const __PROD__ = project.env === 'production';
const config = {
entry: {
normalize: [
inProjectSrc('normalize'),
],
main: [
inProjectSrc(project.main),
],
},
devtool: project.sourcemaps ? 'source-map' : false,
output: {
path: inProject(project.outDir),
filename: '[name].js',
publicPath: project.publicPath,
},
resolve: {
modules: [
inProject(project.srcDir),
'node_modules',
],
extensions: ['*', '.js', '.jsx', '.json'],
},
externals: project.externals,
module: {
rules: [],
},
plugins: [
new webpack.DefinePlugin(Object.assign({
'process.env': { NODE_ENV: JSON.stringify(project.env) },
__DEV__,
__TEST__,
__PROD__,
}, project.globals)),
],
};
// JavaScript
// ------------------------------------
config.module.rules.push({
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
query: {
cacheDirectory: true,
plugins: [
'babel-plugin-transform-decorators-legacy',
'babel-plugin-transform-class-properties',
'babel-plugin-syntax-dynamic-import',
[
'babel-plugin-transform-runtime',
{
helpers: true,
polyfill: false, // we polyfill needed features in src/normalize.js
regenerator: true,
},
],
[
'babel-plugin-transform-object-rest-spread',
{
useBuiltIns: true, // we polyfill Object.assign in src/normalize.js
},
],
],
presets: [
'babel-preset-react',
['babel-preset-env', {
modules: false,
targets: {
ie9: true,
},
uglify: true,
}],
],
},
}],
});
// Styles
// ------------------------------------
const extractStyles = new ExtractTextPlugin({
filename: 'styles/[name].css',
allChunks: true,
disable: __DEV__,
});
config.module.rules.push({
test: /\.(sass|scss)$/,
loader: extractStyles.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
sourceMap: project.sourcemaps,
minimize: {
autoprefixer: {
add: true,
remove: true,
browsers: ['last 2 versions'],
},
discardComments: {
removeAll: true,
},
discardUnused: false,
mergeIdents: false,
reduceIdents: false,
safe: true,
sourcemap: project.sourcemaps,
},
},
},
{
loader: 'sass-loader',
options: {
sourceMap: project.sourcemaps,
includePaths: [
inProjectSrc('styles'),
],
},
},
],
}),
});
config.plugins.push(extractStyles);
// Images
// ------------------------------------
config.module.rules.push({
test: /\.(png|jpg|gif|jpeg|JPG|)$/,
loader: 'url-loader',
options: {
limit: 8192,
},
})
// Fonts
// ------------------------------------
;[
['woff', 'application/font-woff'],
['woff2', 'application/font-woff2'],
['otf', 'font/opentype'],
['ttf', 'application/octet-stream'],
['eot', 'application/vnd.ms-fontobject'],
['svg', 'image/svg+xml'],
].forEach((font) => {
const extension = font[0];
const mimetype = font[1];
config.module.rules.push({
test: new RegExp(`\\.${extension}$`),
loader: 'url-loader',
options: {
name: 'fonts/[name].[ext]',
limit: 10000,
mimetype,
},
});
});
// HTML Template
// ------------------------------------
config.plugins.push(new HtmlWebpackPlugin({
template: inProjectSrc('index.html'),
inject: true,
minify: {
collapseWhitespace: true,
},
}));
// Development Tools
// ------------------------------------
if (__DEV__) {
config.entry.main.push(
`webpack-hot-middleware/client.js?path=${config.output.publicPath}__webpack_hmr`
);
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
);
}
// Bundle Splitting
// ------------------------------------
if (!__TEST__) {
const bundles = ['normalize', 'manifest'];
if (project.vendors && project.vendors.length) {
bundles.unshift('vendor');
config.entry.vendor = project.vendors;
}
config.plugins.push(new webpack.optimize.CommonsChunkPlugin({ names: bundles }));
}
// Production Optimizations
// ------------------------------------
if (__PROD__) {
config.plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false,
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: !!config.devtool,
comments: false,
compress: {
warnings: false,
screw_ie8: true,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
},
})
);
}
module.exports = config;