I am new to React. I have a React application which recently pushed to Production. There were some frontend fixes and again pushed to production. But it is showing the same page after new build. It is taking from cache. But if i remove cache, it is working.
Using react boilerplate (https://github.com/react-boilerplate/react-boilerplate)
I tried searching for Service worker and res.header(cache-control). But nothing seems to be worked.
Below is my webpack prod file
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const OfflinePlugin = require('offline-plugin');
const { HashedModuleIdsPlugin } = require('webpack');
const webpack = require('webpack');
const S3Plugin = require('webpack-s3-plugin');
// const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const config = require('../../app/config');
module.exports = require('./webpack.base.babel')({
mode: 'production',
// In production, we skip all hot-reloading stuff
entry: [
require.resolve('react-app-polyfill/ie11'),
path.join(process.cwd(), 'app/app.js'),
],
// Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].chunk.js',
},
// optimization: {
// minimize: true,
// minimizer: [
// new TerserPlugin({
// terserOptions: {
// warnings: false,
// compress: {
// comparisons: false,
// },
// parse: {},
// mangle: true,
// output: {
// comments: false,
// ascii_only: true,
// },
// },
// parallel: true,
// cache: true,
// sourceMap: true,
// }),
// ],
// nodeEnv: 'production',
// sideEffects: true,
// concatenateModules: true,
// splitChunks: {
// chunks: 'all',
// minSize: 30000,
// minChunks: 1,
// maxAsyncRequests: 5,
// maxInitialRequests: 3,
// name: true,
// cacheGroups: {
// commons: {
// test: /[\\/]node_modules[\\/]/,
// name: 'vendor',
// chunks: 'all',
// },
// main: {
// chunks: 'all',
// minChunks: 2,
// reuseExistingChunk: true,
// enforce: true,
// },
// },
// },
// runtimeChunk: true,
// },
plugins: [
// Minify and optimize the index.html
new HtmlWebpackPlugin({
template: 'app/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
new S3Plugin({
exclude: /.*\.html$/,
s3Options: {
accessKeyId: config.aws.s3.aws_access_key_id,
secretAccessKey: config.aws.s3.aws_secret_access_key,
region: 'us-west-2',
},
s3UploadOptions: {
Bucket: config.aws.s3.bucket,
},
}),
// Put it in the end to capture all the HtmlWebpackPlugin's
// assets manipulations and do leak its manipulations to HtmlWebpackPlugin
new OfflinePlugin({
relativePaths: false,
publicPath: '/',
appShell: '/',
// No need to cache .htaccess. See http://mxs.is/googmp,
// this is applied before any match in `caches` section
excludes: ['.htaccess'],
caches: {
main: [':rest:'],
// All chunks marked as `additional`, loaded after main section
// and do not prevent SW to install. Change to `optional` if
// do not want them to be preloaded at all (cached only when first loaded)
additional: ['*.chunk.js'],
},
// Removes warning for about `additional` section usage
safeToUseOptionalCaches: true,
AppCache: false,
ServiceWorker: {
events: true,
publicPath: '/sw.js',
navigateFallbackURL: '/',
prefetchRequest: {},
},
// Hack
// This is to skip the warning that OfflinePlugin throws if sw registration is not done
__tests: {
ignoreRuntime: true,
},
}),
new CompressionPlugin({
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8,
}),
new WebpackPwaManifest({
name: 'React Boilerplate',
short_name: 'React BP',
description: 'My React Boilerplate-based project!',
background_color: '#fafafa',
theme_color: '#b1624d',
inject: true,
ios: true,
icons: [
{
src: path.resolve('app/images/icon-512x512.png'),
sizes: [72, 96, 128, 144, 192, 384, 512],
},
{
src: path.resolve('app/images/icon-512x512.png'),
sizes: [120, 152, 167, 180],
ios: true,
},
],
}),
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
new HashedModuleIdsPlugin({
hashFunction: 'sha256',
hashDigest: 'hex',
hashDigestLength: 20,
}),
],
performance: {
assetFilter: assetFilename =>
!/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
},
});```
Use webpack and use hashing (and append it to file names) to generate the files.
Hence whenever you rebuild the app and deploy it on production those hashed files wont be present in the cache and you wont have the cache problem anymore.
You can disable caching if you don't want it but I guess here you wanted to refresh the cache on new deploys, pls try below
app/app.js - at the end of file
if (process.env.NODE_ENV === 'production') {
require('offline-plugin/runtime').install(); // eslint-disable-line global-require
}
replace with
if (process.env.NODE_ENV === 'production') {
const runtime = require('offline-plugin/runtime'); // eslint-disable-line global-require
runtime.install({
onUpdating: () => {
console.log('SW Event:', 'onUpdating');
},
onUpdateReady: () => {
console.log('SW Event:', 'onUpdateReady');
// Tells to new SW to take control immediately
runtime.applyUpdate();
},
onUpdated: () => {
console.log('SW Event:', 'onUpdated');
// Reload the webpage to load into the new version
window.location.reload();
},
onUpdateFailed: () => {
console.log('SW Event:', 'onUpdateFailed');
},
});
}
internals/webpack/webpack.prod.babel.js
add line 90, under new OfflinePlugin({
ServiceWorker: {
events: true,
},
Related
I am trying to figure out how to use CRACO (https://github.com/gsoft-inc/craco) to disable file chunking in create react app.
I have created the following craco.config.js:
// craco.config.js
module.exports = {
output: {
fileName: 'static/js/bundle.js',
},
}
But it doesn't have any effect. What should the config look like to disable file chunking in CRA with CRACO?
EDIT: To disable chunking completely I believe this might do it.
Source: https://github.com/facebook/create-react-app/issues/5306#issuecomment-650737697
// craco.config.js
module.exports = {
webpack: {
configure: {
optimization: {
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false
},
},
},
},
},
}
ORIGNIAL:
Maybe this could help?
module.exports = {
webpack: {
configure: {
output: {
filename: 'static/js/bundle.js',
},
optimization: {
runtimeChunk: false,
splitChunks: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
// vendor chunk
},
},
},
},
},
plugins: [
{
plugin: require('craco-plugin-scoped-css'),
},
],
}
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;
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.
Why my react app fails on production build after upgrading to react 16 ?
After upgrading react to version 16 my app stoped working on production build, when running development works fine. If I downgrade to React 15.6 it still works fine on both prod and dev enviroments.
I am using: "webpack": "^3.5.6", and "react": "^16.0.0",
I am getting the following error:
Uncaught ReferenceError: require is not defined
My webpack prod configuration:
const path = require('path');
const merge = require("webpack-merge");
const webpack = require("webpack");
const config = require("./webpack.base.babel");
const OfflinePlugin = require('offline-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(config, {
// devtool: "nosources-source-map",
devtool: "source-map",
// In production, we skip all hot-reloading stuff
entry: [
'babel-polyfill', // Needed for redux-saga es6 generator support
path.join(process.cwd(), 'src/client/app.js'), // Start with app.js
],
performance: {
assetFilter: (assetFilename) => !(/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename)),
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true
}),
new HtmlWebpackPlugin({
template: "src/client/index.html",
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
// Shared code
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
children: true,
minChunks: 2,
async: true,
}),
// Avoid publishing files when compilation fails
new webpack.NoEmitOnErrorsPlugin(),
// Put it in the end to capture all the HtmlWebpackPlugin's
// assets manipulations and do leak its manipulations to HtmlWebpackPlugin
new OfflinePlugin({
relativePaths: false,
publicPath: '/',
// No need to cache .htaccess. See http://mxs.is/googmp,
// this is applied before any match in `caches` section
excludes: ['.htaccess'],
caches: {
main: [':rest:'],
// All chunks marked as `additional`, loaded after main section
// and do not prevent SW to install. Change to `optional` if
// do not want them to be preloaded at all (cached only when first loaded)
additional: ['*.chunk.js'],
},
// Removes warning for about `additional` section usage
safeToUseOptionalCaches: true,
AppCache: false,
}),
]
});
How can i fix it ?
webpack.base.babel.js
// Common Webpack configuration used by webpack.config.development and webpack.config.production
const path = require("path");
const webpack = require("webpack");
const autoprefixer = require("autoprefixer");
const e2c = require("electron-to-chromium");
const GLOBALS = require('../bin/helpers/globals');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const isProd = process.env.NODE_ENV === 'production';
const postcssLoaderOptions = {
plugins: [
autoprefixer({
browsers: e2c.electronToBrowserList("1.4")
}),
],
sourceMap: !isProd,
};
GLOBALS['process.env'].__CLIENT__ = true;
module.exports = {
target: 'web', // Make web variables accessible to webpack, e.g. window
output: {
filename: 'js/[name].[hash].js',
chunkFilename: 'js/[name].[hash].chunk.js',
path: path.resolve(process.cwd(), 'build'),
publicPath: "/"
},
resolve: {
modules: ["node_modules"],
alias: {
client: path.resolve(process.cwd(), "src/client"),
shared: path.resolve(process.cwd(), "src/shared"),
server: path.resolve(process.cwd(), "src/server")
},
extensions: [".js", '.jsx', ".json", ".scss"],
mainFields: ["browser", "module", 'jsnext:main', "main"],
},
plugins: [
new webpack.NormalModuleReplacementPlugin(
/\/Bundles.js/,
'./AsyncBundles.js'
),
new webpack.IgnorePlugin(/vertx/),
new webpack.ProvidePlugin({
Promise: 'imports-loader?this=>global!exports-loader?global.Promise!es6-promise',
fetch: "imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch", // fetch API
$: "jquery",
jQuery: "jquery",
}),
new webpack.DefinePlugin(GLOBALS),
new ExtractTextPlugin({
filename: "css/[name].[hash].css",
disable: false,
allChunks: true
})
],
module: {
noParse: /\.min\.js$/,
rules: [
// JavaScript / ES6
{
test: /\.(js|jsx)?$/,
include: [
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
],
exclude: /node_modules/,
use: "babel-loader"
},
// Json
{
test: /\.json$/,
use: 'json-loader',
},
//HTML
{
test: /\.html$/,
include: [
path.resolve(process.cwd(), "src/client"),
],
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
},
// Images
// Inline base64 URLs for <=8k images, direct URLs for the rest
{
test: /\.(png|jpg|jpeg|gif|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: "url-loader",
options: {
limit: 8192,
name: "images/[name].[ext]?[hash]"
}
}
},
// Fonts
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff'
}
}
},
{
test: /\.(ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: 'file-loader'
},
// Styles
{
test: /\.scss$/,
include: [
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
options: {
sourceMap: true,
modules: true,
importLoaders: 1,
localIdentName: '[local]_[hash:base64:3]'
}
},
{
loader: "postcss-loader",
options: postcssLoaderOptions
},
{
loader: "sass-loader",
options: {
sourceMap: true,
outputStyle: "compressed"
}
}
]
})
},
]
}
};
The fix was rly simple.
I just needed to remove this line noParse: /\.min\.js/
Which does :
Prevent webpack from parsing any files matching the given regular
expression(s). Ignored files should not have calls to import, require,
define or any other importing mechanism.
Building a ReactJS application with Webpack. Recently interested in using code splitting to reduce app size.
I've tried implementing a custom HOC that wrapped System.import():
/* async/index.tsx */
... at a very high level looked like...
class Async extends React ... {
componentWillMount() {
this.props.load.then(c => {
this.component = c;
this.setState({loaded:true});
}
}
render() {
return this.component ? <this.component.default {...this.props} /> : <span />;
}
}
/* async/foo/index.tsx */
import Async from 'async';
const Foo = (props) => (
<Async
{...props}
load={System.import('async/foo/body')}
/>);
class Foo ... {
...
render = () => <Foo myProp={'bar'} />;
}
Currently I'm trying react-loadable (https://github.com/thejameskyle/react-loadable), a package that does essentially the same thing but with some extra bells n whistles.
Problem
Both methods work fine locally, but do not work when deployed. Webpack configurations are derived from React-Starter-App, from early March 2017. My gut tells me that the webpack config is the source of the problem, but I'm not sure how to debug this.
Dev Config (works)
/* relevant configs */
module.exports = {
entry: [
require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
paths.appIndexJs
],
output: {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/bundle.js',
publicPath: publicPath
},
...
plugins: [
new ExtractTextPlugin({filename: 'style.css', allChunks: true}),
new InterpolateHtmlPlugin(env.raw),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
new BundleAnalyzerPlugin(),
new webpack.DefinePlugin(env.stringified),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true
}),
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
]
}
Staging Config (not working)
module.exports = {
bail: true,
devtool: 'source-map',
entry: [
require.resolve('./polyfills'),
paths.appIndexJs
],
output: {
path: paths.appBuildStaging,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath,
},
resolve: {
modules: ['node_modules', paths.appNodeModules].concat(paths.nodePaths).concat(paths.appSrc),
extensions: ['.ts', '.tsx', '.scss', '.js', '.json', '.jsx']
},
...
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: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
// this assumes your vendor imports exist in the node_modules directory
return module.context && module.context.indexOf('node_modules') !== -1;
}
}),
//CommonChunksPlugin will now extract all the common modules from vendor and main bundles
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest' //But since there are no more common modules between them we end up with just the runtime code included in the manifest file
}),
new BundleAnalyzerPlugin(),
new webpack.DefinePlugin(env.stringified),
new webpack.optimize.UglifyJsPlugin({
compress: {
screw_ie8: true, //. React doesn't support IE8
warnings: false,
drop_console: false,
},
mangle: {
screw_ie8: true,
},
output: {
comments: false,
screw_ie8: true,
},
sourceMap: true,
}),
new ExtractTextPlugin({
filename: cssFilename,
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
]
}
Errors:
react-loadable swallows all errors (palm + face) so I'm not able to provide useful errors from that code.
My custom component would throw an error in the bootstrap loader bootstrap 6a12c6c…:54 Uncaught (in promise) TypeError: Cannot read property 'call' of undefined
In the network traffic for my custom HOC, the extra bundle is not loaded.
In the network traffic for react-loadable, the bundle is loaded, but it is never processed.
So after all this time, after upgrading Typescript and Webpack, it turns out that using the CommonsChunk plugin was screwing it up somehow.
Have not yet investigated why, but commenting out the following worked:
// new webpack.optimize.CommonsChunkPlugin({
// name: 'vendor',
// minChunks: function (module) {
// return module.context && module.context.indexOf('node_modules') !== -1;
// }
// }),