Can't get React Hot Loader to work - reactjs

I am trying to config react-hot-loader into my app. It's built on webpack 2, webpack-dev-middleware, browser-sync, express and doing server side rendering.
Here's my webpack.config
require('dotenv').config()
import path from 'path'
import webpack from 'webpack'
import extend from 'extend'
import AssetsPlugin from 'assets-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import bundles from '../src/bundles'
import merge from 'lodash/merge'
import fs from 'fs'
const isDebug = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');
const GLOBALS = {
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
'process.env.MORTGAGE_CALCULATOR_API': process.env.MORTGAGE_CALCULATOR_API ? `"${process.env.MORTGAGE_CALCULATOR_API}"` : null,
'process.env.API_HOST': process.env.BROWSER_API_HOST ? `"${process.env.BROWSER_API_HOST}"` : process.env.API_HOST ? `"${process.env.API_HOST}"` : null,
'process.env.GOOGLE_ANALYTICS_ID': process.env.GOOGLE_ANALYTICS_ID ? `"${process.env.GOOGLE_ANALYTICS_ID}"` : null,
'process.env.OMNITURE_SUITE_ID': process.env.OMNITURE_SUITE_ID ? `"${process.env.OMNITURE_SUITE_ID}"` : null,
'process.env.COOKIE_DOMAIN': process.env.COOKIE_DOMAIN ? `"${process.env.COOKIE_DOMAIN}"` : null,
'process.env.FEATURE_FLAG_BAZAAR_VOICE': process.env.FEATURE_FLAG_BAZAAR_VOICE ? `"${process.env.FEATURE_FLAG_BAZAAR_VOICE}"` : null,
'process.env.FEATURE_FLAG_REALTIME_RATING': process.env.FEATURE_FLAG_REALTIME_RATING ? `"${process.env.FEATURE_FLAG_REALTIME_RATING}"` : null,
'process.env.SALE_RESULTS_PAGE_FLAG': process.env.SALE_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RESULTS_PAGE_FLAG}"` : null,
'process.env.SALE_RELOADED_RESULTS_PAGE_FLAG': process.env.SALE_RELOADED_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RELOADED_RESULTS_PAGE_FLAG}"` : null,
'process.env.TRACKER_DOMAIN': process.env.TRACKER_DOMAIN ? `"${process.env.TRACKER_DOMAIN}"` : null,
'process.env.USER_SERVICE_ENDPOINT': process.env.USER_SERVICE_ENDPOINT ? `"${process.env.USER_SERVICE_ENDPOINT}"` : null,
__DEV__: isDebug
};
//
// Common configuration chunk to be used for both
// client-side (client.js) and server-side (server.js) bundles
// -----------------------------------------------------------------------------
const config = {
output: {
publicPath: '/blaze-assets/',
},
cache: isDebug,
stats: {
colors: true,
reasons: isDebug,
hash: isVerbose,
version: isVerbose,
timings: true,
chunks: isVerbose,
chunkModules: isVerbose,
cached: isVerbose,
cachedAssets: isVerbose,
},
plugins: [
new ExtractTextPlugin({
filename: isDebug ? '[name].css' : '[name].[chunkhash].css',
allChunks: true,
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: isDebug,
}),
],
resolve: {
extensions: ['.webpack.js', '.web.js', '.js', '.jsx', '.json'],
modules: [
path.resolve('./src'),
'node_modules',
]
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, '../src'),
],
loader: 'babel-loader',
}, {
test: /\.(scss|css)$/,
exclude: ['node_modules'],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'sass-loader',
]
})
}, {
test: /\.txt$/,
loader: 'raw-loader',
}, {
test: /\.(otf|png|jpg|jpeg|gif|svg|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000',
}, {
test: /\.(eot|ttf|wav|mp3)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
}, {
test: /\.jade$/,
loader: 'jade-loader',
}
],
},
};
//
// Configuration for the client-side bundles
// -----------------------------------------------------------------------------
let clientBundles = {}
Object.keys(bundles).forEach(function (bundle) {
clientBundles[bundle] = [
'bootstrap-loader',
`./src/bundles/${bundle}/index.js`
]
})
merge(
clientBundles,
{
'embedWidget': ['./src/components/Widgets/EmbedWidget/widgetLoader.js']
},
)
const clientConfig = extend(true, {}, config, {
entry: clientBundles,
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: isDebug ? '[name].js' : '[name].[chunkhash].js',
chunkFilename: isDebug ? '[name].chunk.js' : '[name].[chunkhash].chunk.js',
},
node: {
fs: "empty"
},
// Choose a developer tool to enhance debugging
// http://webpack.github.io/docs/configuration.html#devtool
// devtool: isDebug ? 'cheap-module-source-map' : false,
plugins: [
...config.plugins,
...(isDebug ? [
new webpack.EvalSourceMapDevToolPlugin({
filename: '[file].map',
exclude: /\.(css)($)/i,
}),
] : []),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': true
}),
...(!isDebug ? [
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
screw_ie8: true,
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
warnings: isVerbose,
unused: true,
dead_code: true,
},
}),
new webpack.optimize.AggressiveMergingPlugin(),
] : []),
new AssetsPlugin({
path: path.join(__dirname, '../build'),
filename: 'assets.json',
prettyPrint: true,
}),
],
});
//
// Configuration for the server-side bundle (server.js)
// -----------------------------------------------------------------------------
var srcDirs = {};
fs.readdirSync('src').forEach(function(path) {
srcDirs[path] = true
});
function isExternalFile(context, request, callback) {
var isExternal = request.match(/^[#a-z][a-z\/\.\-0-9]*$/i) && !srcDirs[request.split("/")[0]]
callback(null, Boolean(isExternal));
}
const serverConfig = extend(true, {}, config, {
entry: './src/server.js',
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: '../../server.js',
libraryTarget: 'commonjs2',
},
target: 'node',
externals: [
/^\.\/assets\.json$/,
isExternalFile
],
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
devtool: isDebug ? 'cheap-module-source-map' : 'source-map',
plugins: [
...config.plugins,
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': false,
'process.env.API_HOST': process.env.API_HOST ? `"${process.env.API_HOST}"` : null
}),
new webpack.NormalModuleReplacementPlugin(/\.(scss|css|eot|ttf|woff|woff2)$/, 'node-noop'),
new webpack.BannerPlugin({
banner: `require('dotenv').config(); require('newrelic'); require('source-map-support').install();`,
raw: true,
entryOnly: false
})
],
});
export default [clientConfig, serverConfig];
start.js file
import Browsersync from 'browser-sync'
import webpack from 'webpack'
import webpackMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import WriteFilePlugin from 'write-file-webpack-plugin'
import run from './run'
import runServer from './runServer'
import webpackConfig from './webpack.config'
import clean from './clean'
import copy from './copy'
const isDebug = !process.argv.includes('--release')
const [, serverConfig] = webpackConfig
process.argv.push('--watch')
/**
* Launches a development web server with "live reload" functionality -
* synchronizing URLs, interactions and code changes across multiple devices.
*/
async function start () {
await run(clean)
await run(copy.bind(undefined, { watch: true }))
await new Promise((resolve) => {
serverConfig.plugins.push(new WriteFilePlugin({ log: false }))
// Patch the client-side bundle configurations
// to enable Hot Module Replacement (HMR) and React Transform
webpackConfig.filter((x) => x.target !== 'node').forEach((config) => {
/* eslint-disable no-param-reassign */
Object.keys(config.entry).forEach((entryKey) => {
if (!Array.isArray(config.entry[entryKey])) {
config.entry[entryKey] = [config.entry[entryKey]]
}
config.entry[entryKey].unshift('react-hot-loader/patch', 'webpack-hot-middleware/client')
})
if (config.output.filename) {
config.output.filename = config.output.filename.replace('[chunkhash]', '[hash]')
}
if (config.output.chunkFilename) {
config.output.chunkFilename = config.output.chunkFilename.replace('[chunkhash]', '[hash]')
}
config.plugins.push(new webpack.HotModuleReplacementPlugin())
config.plugins.push(new webpack.NoEmitOnErrorsPlugin())
config
.module
.rules
.filter((x) => x.loader === 'babel-loader')
.forEach((x) => (x.query = {
...x.query,
cacheDirectory: true,
presets: [
['es2015', {modules: false}],
'stage-0',
'react'
],
plugins: [
...(x.query ? x.query.plugins : []),
[
'react-hot-loader/babel',
],
],
}))
/* eslint-enable no-param-reassign */
})
const bundler = webpack(webpackConfig)
const wpMiddleware = webpackMiddleware(bundler, {
// IMPORTANT: webpack middleware can't access config,
// so we should provide publicPath by ourselves
publicPath: webpackConfig[0].output.publicPath,
// Pretty colored output
stats: webpackConfig[0].stats,
// For other settings see
// https://webpack.github.io/docs/webpack-dev-middleware
})
const hotMiddleware = webpackHotMiddleware(bundler.compilers[0])
let handleBundleComplete = async () => {
handleBundleComplete = (stats) => !stats.stats[1].compilation.errors.length && runServer()
const server = await runServer()
const bs = Browsersync.create()
bs.init({
...isDebug ? {} : { notify: false, ui: false },
proxy: {
target: server.host,
middleware: [wpMiddleware, hotMiddleware],
proxyOptions: {
xfwd: true,
},
},
open: false,
files: ['build/content/**/*.*'],
}, resolve)
}
bundler.plugin('done', (stats) => handleBundleComplete(stats))
})
}
export default start
To hot load reducers, I have changed my code like this,
const configureStore = (initialState = {}) => {
const store = createStore(
// storage.reducer is what merges storage state into redux state
storage.reducer(rootReducer),
inflateDecorators(initialState),
applyMiddleware(...middleware)
)
if (module.hot) {
module.hot.accept('./reducers', () => {
const nextRootReducer = require('./reducers')
store.replaceReducer(nextRootReducer)
})
}
return store
}
We have multiple bundles, so there is common function to handle the entry logic,
import 'babel-polyfill'
import React from 'react'
import ReactDOM from 'react-dom'
import FastClick from 'fastclick'
import { Provider } from 'react-redux'
import { setUrl } from 'actions'
import Location from '../../libs/Location'
import configureStore, { loadFromLocalStorage } from '../../configureStore'
import { AppContainer } from 'react-hot-loader'
const initialState = window.__INITIAL_STATE__
const store = configureStore(initialState)
function runner (createBody) {
return function () {
// Make taps on links and buttons work fast on mobiles
FastClick.attach(document.body)
const component = (
<AppContainer>
<Provider store={store}>
{createBody()}
</Provider>
</AppContainer>
)
if (!initialState) {
store.dispatch(setUrl(`${Location.location.pathname}${Location.location.search}`))
}
Location.listen((location) => {
store.dispatch(setUrl(`${location.pathname}${location.search}`))
})
ReactDOM.render(component, document.getElementById('app'))
// only apply stored state after first render
// this allows serverside and clientside rendering to agree on initial state
loadFromLocalStorage(store)
}
}
export default function run (createBody) {
if (['complete', 'loaded', 'interactive'].includes(document.readyState) && document.body) {
runner(createBody)()
} else {
document.addEventListener('DOMContentLoaded', runner(createBody), false)
}
}
This is one of the bundles entry point where the above common function is called,
import run from '../util/run'
import createBody from './body'
run(createBody)
if (module.hot) {
module.hot.accept('./body', () => run(createBody))
}
Not sure what I am missing still, I tried to follow a few blog post and react hot loader docs but I couldn't get it to working.

Thought it would be helpful for people who have similar kind of setup, even in webpack 2 for react hot loader to work your entry point code should be something like this,
if (module.hot) {
module.hot.accept('./body', () => {
const body = require('./body').default
run(body)
})
}
As I am using extract-text-webpack-plugin it is not possible to hot reload css changes. As I am already using browser-sync, the simple work around is to use write-file-webpack-plugin to write the css files out and let browser-sync listen to the changes.

Related

CSS modules not working in loadable-components in server side rendering

I am trying to add the loadable components library for code splitting in my React universal app. I have CSS modules in my project, and it used to work fine. But then I added the loadable component library for code splitting. Now the server is working, but the CSS is not, and the pages are loading without CSS. I have checked the stats.json file and it is missing the CSS files.
webpack.config.js:
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
// const ESLintPlugin = require('eslint-webpack-plugin');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const ReactRefreshWebpackPlugin = require('#pmmmwh/react-refresh-webpack-plugin');
const LoadablePlugin = require('#loadable/webpack-plugin')
const postcssNormalize = require('postcss-normalize');
const appPackageJson = require(paths.appPackageJson);
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const webpackDevClientEntry = require.resolve(
'react-dev-utils/webpackHotDevClient'
);
const reactRefreshOverlayEntry = require.resolve(
'react-dev-utils/refreshOverlayInterop'
);
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);
const useTypeScript = fs.existsSync(paths.appTsConfig);
const swSrc = paths.swSrc;
const cssRegex = /\.css$/;
const cssModuleRegex = /\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}
try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
postcssNormalize(),
],
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
root: paths.appSrc,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
}
);
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
entry:
isEnvDevelopment && !shouldUseReactRefresh
? [
webpackDevClientEntry,
paths.appIndexJs,
]
: paths.appIndexJs,
output: {
path: isEnvProduction ? paths.appBuild : undefined,
pathinfo: isEnvDevelopment,
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
futureEmitAssets: true,
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: paths.publicUrlOrPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
globalObject: 'this',
},
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
inline: 2,
},
mangle: {
safari10: true,
},
// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,
keep_fnames: isEnvProductionProfile,
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
sourceMap: shouldUseSourceMap,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
inline: false,
annotation: true,
}
: false,
},
cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
},
}),
],
splitChunks: {
chunks: 'all',
name: false,
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
},
resolve: {
modules: ['node_modules', paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
'react-native': 'react-native-web',
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
},
plugins: [
new ModuleScopePlugin(paths.appSrc, [
paths.appPackageJson,
reactRefreshOverlayEntry,
]),
],
},
resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
oneOf: [
{
test: [/\.avif$/],
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
mimetype: 'image/avif',
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
isEnvDevelopment &&
shouldUseReactRefresh &&
require.resolve('react-refresh/babel'),
].filter(Boolean),
cacheCompression: false,
compact: isEnvProduction,
},
},
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
}),
sideEffects: true,
},
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
}),
},
{
loader: require.resolve('file-loader'),
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [
new LoadablePlugin({filename:'../dist/loadable-stats.json',writeToDisk:true}),
// Generates an `index.html` file with the <script> injected.
new LoadablePlugin(),
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: {
entry: webpackDevClientEntry,
sockIntegration: false,
},
}),
isEnvDevelopment && new CaseSensitivePathsPlugin(),
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
isEnvProduction &&
fs.existsSync(swSrc) &&
new WorkboxWebpackPlugin.InjectManifest({
swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
template/pwa/issues/13#issuecomment-722667270
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: isEnvDevelopment,
checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
tsconfig: paths.appTsConfig,
reportFiles: [
'../**/src/**/*.{ts,tsx}',
'**/src/**/*.{ts,tsx}',
'!**/src/**/__tests__/**',
'!**/src/**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
silent: true,
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
].filter(Boolean),
node: {
module: 'empty',
dgram: 'empty',
dns: 'mock',
fs: 'empty',
http2: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
};
server.js:
// import thunk from 'redux-thunk';
import { createReactAppExpress } from '#cra-express/core';
import { getInitialData } from '#cra-express/router-prefetcher';
import { HelmetProvider } from 'react-helmet-async';
// import Cookies from 'cookies';
import { getStoredState, persistCombineReducers } from 'redux-persist';
// import { CookieStorage, NodeCookiesWrapper } from 'redux-persist-cookie-storage';
import autoMergeLevel1 from 'redux-persist/lib/stateReconciler/autoMergeLevel1';
import storage from "redux-persist/lib/storage";
// import StyleContext from 'isomorphic-style-loader/StyleContext'
import routes from '../src/routes';
import {store} from '../src/index'
import {ChunkExtractor,ChunkExtractorManager} from '#loadable/server'
const path = require('path');
const React = require('react');
const { Provider } = require('react-redux');
const { StaticRouter } = require('react-router-dom');
const { createStore, applyMiddleware ,compose} = require('redux');
const { default: App } = require('../src/App');
const { default: reducer } = require('../src/redux/reducers');
const clientBuildPath = path.resolve(__dirname, '../client');
const statsFile=path.resolve(__dirname,'../dist/loadable-stats.json')
let tag = '';
//let store;
let AppClass = App;
let serverData;
let helmetCtx;
// console.log("REDUCERS",reducer)
const app = createReactAppExpress({
clientBuildPath,
universalRender: handleUniversalRender,
onFinish(req, res, html) {
const { helmet } = helmetCtx;
const helmetTitle = helmet.title.toString();
const helmetMeta = helmet.meta.toString();
const newHtml = html
.replace('{{HELMET_TITLE}}', helmetTitle)
.replace('{{HELMET_META}}', helmetMeta);
res.send(newHtml);
},
onEndReplace(html) {
const state = store.getState();
//console.log("----SERVER getState----", store.getState());
return html.replace(
'{{SCRIPT}}',
`${tag}<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(state).replace(
/</g,
'\\u003c'
)};
window.__INITIAL_DATA__ = ${JSON.stringify(serverData).replace(
/</g,
'\\u003c'
)};
</script>`
);
}
});
function handleUniversalRender(req, res) {
const context = {};
helmetCtx = {};
// const cookieJar = new NodeCookiesWrapper(new Cookies(req, res));
const persistConfig = {
key: 'root',
storage: storage,
// storage: new CookieStorage(cookieJar),
stateReconciler: autoMergeLevel1,
};
let preloadedState;
getStoredState(persistConfig)
.then(preloadedState => {
//console.log("SERVER Preloded State", preloadedState);
})
try {
preloadedState = {
test: 'presisited Data'
};
} catch (e) {
preloadedState = {};
}
const rootReducer = persistCombineReducers(persistConfig, reducer);
/* store = createStore(
rootReducer,
preloadedState,
applyMiddleware(thunk)
);*/
return getInitialData(req, res,routes)
.then(data => {
const css = new Set();
const insertCss = (...styles) => styles.forEach(style => css.add(style._getCss()));
const extractor=new ChunkExtractor({statsFile})
const scriptTags = extractor.getScriptTags()
serverData = data;
// console.log("CSS FILES", scriptTags);
const app = (
<HelmetProvider context={helmetCtx}>
<StaticRouter location={req.url} context={context}>
<Provider store={store}>
{/* <StyleContext.Provider value={{ insertCss }}> */}
<ChunkExtractorManager extractor={extractor}>
<AppClass routes={routes} initialData={data} store={store}/>
</ChunkExtractorManager>
{/* </StyleContext.Provider> */}
</Provider>
</StaticRouter>
</HelmetProvider>
);
return app;
})
.catch(err => {
console.error(err);
res.send(500);
});
}
if (module.hot) {
module.hot.accept('../src/App', () => {
const { default: App } = require('../src/App');
AppClass = App;
console.log('✅ Server hot reloaded App');
});
module.hot.accept('../src/routes', () => {
console.log('✅ Server hot reloaded routes');
});
}
export default app;
css modules works fine with client side and worked fine without code splitting in server side but not working with loadable components. i am using cra-universal library for server side rendering.
can someone please help me with this issue and it will be greatly appreciated.

Webpack 5 - "Unexpected token: punc (.)" after import axios

I am getting a strange issue on bundling webpack for production environment.
Unexpected token: punc (.)
This happens only when a React component imports axios
import React from "react";
import axios from "axios"; // <---------------
class SimpleComponent extends React.Component {
render() {
return (
<section className="bg-white py-16">
Simple
</section>
)
}
}
export default SimpleComponent
This results in the following error:
$ npm run build
ERROR in static/main.b394a534fa5736fe90cc.js from Terser
Unexpected token: punc (.) [static/main.b394a534fa5736fe90cc.js:18978,6]
at js_error (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:546:11)
at croak (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1264:9)
at token_error (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1272:9)
at unexpected (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1278:9)
at statement (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1397:17)
at _embed_tokens_wrapper (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1322:26)
at block_ (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:2155:20)
at statement (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1386:29)
at _embed_tokens_wrapper (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:1322:26)
at if_ (/home/franciscocarvalho/code/oss/axios-issue-example-with-webpack5/node_modules/terser-webpack-plugin/node_modules/terser/dist/bundle.min.js:2141:21)
To Reproduce
Please fork the following axios-issue-example-with-webpack5 repository
Perform npm install
Run npm run build
Expected behavior
None error after bundle
Environment
Axios Version [0.21.1]
Node.js Version [v12.20.1]
Additional Library Versions [React 17.0.1, Webpack 5.15.0]
webpack.config
const path = require("path")
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const {CleanWebpackPlugin} = require("clean-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const Dotenv = require("dotenv-webpack")
const ROOT_DIR = path.resolve(__dirname, "..")
module.exports = {
mode: "production",
entry: [
path.resolve(ROOT_DIR, "./src/index.js"),
path.resolve(ROOT_DIR, "./src/styles/styles.scss")
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
"babel-loader",
{
loader: "eslint-loader",
options: {
configFile: path.resolve(ROOT_DIR, ".eslintrc")
}
}
]
},
{
test: /\.(jpe?g|png|gif|ico|svg|woff|woff2|webp)$/i,
use: [
{
loader: "file-loader",
options: {
name: "static/[name].[ext]"
}
}
]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: true
}
},
'postcss-loader',
{
loader: "sass-loader",
options: {
sassOptions: {
includePaths: [
path.resolve(ROOT_DIR, "node_modules"),
path.resolve(ROOT_DIR, "src/styles/")
]
}
}
}
],
}
]
},
resolve: {
extensions: ["*", ".js", ".jsx", ".scss"],
fallback: {
assert: false,
http: false,
https: false,
zlib: false,
tty: false,
util: false,
fs: false,
net: false,
stream: false
}
},
plugins: [
new Dotenv({
path: path.resolve(__dirname, "..", "./.env")
}),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Advanced React with Webpack Setup",
template: path.resolve(__dirname, "..", "./src/index.ejs")
}),
new MiniCssExtractPlugin({
filename: "static/[name]-[contenthash].css",
}),
new PurgeCSSPlugin({
paths: glob.sync(`${ROOT_DIR}/src/**/*`, {nodir: true}),
}),
],
stats: {
modules: true,
hash: true,
assetsSort: "!size",
children: true
},
output: {
path: path.resolve(__dirname, "..", "dist"),
chunkFilename: 'static/[name].[hash].js',
filename: "static/[name].[hash].js",
chunkLoading: false,
wasmLoading: false
},
optimization: {
minimize: true,
minimizer: [
// For webpack#5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
// `...`,
new CssMinimizerPlugin(),
/*new UglifyJsPlugin({
test: /\.js(\?.*)?$/i,
parallel: true,
})*/
new TerserPlugin({
terserOptions: {
ecma: 6,
compress: {
warnings: true
},
output: {
comments: false,
beautify: false
}
}
})
],
},
}
Problem
Like I said the problem is from debug which has been included by webpack in your built file (the node code part). That code looks like:
function save(namespaces) {
if (null == namespaces) {
delete {}.DEBUG;
} else {
{}.DEBUG = namespaces; // that is the invalid statement that `terser` complains about
}
}
// Plus, the main file `index.js` of `debug`:
if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
// Above code is from here
module.exports = require('./node.js');
}
Solution
Above code of debug for node has been added since you haven't yet told webpack you build for web app so then it won't include that code.
So as long as you set target as web in your configuration, then it would work:
webpack.common.js
module.exports = {
target: 'web',
// ...
}
One more thing I found that your css typeface-roboto you imported requires you set publicPath as output:
webpack.prod.js
module.exports = {
// ...
output: {
// ...
publicPath: '/',
},
}

deploying officejs fabric react word add in

I have a word add in written in typescript using Officejs and office-ui-fabric-react. Everything works fine with the server running locally. I'd like to deploy into AWS S3 with Cloudfront. I'm building with npm run build, and npm run deploy (using deploy command "aws --profile profile-name s3 sync dist/ s3://bucketname". Static web site hosting is enabled in the S3 bucket. All files from the dist directory are seen in the bucket. After inserting the add in into Word with a manifest.xml file that points to the cloudfront endpoint I'm getting the error "Uncaught ReferenceError: React is not defined". The same error occurs when I point directly to the S3 static web endpoint. To see if I've missed anything I deployed a generic create-react-app using the steps above and it runs fine. I'm assuming that the problem lies with my webpack config so I've included that here (common, and prod). I'd be happy to include anything else that's needed. I'm also open to other deployment options if using AWS is causing the problem.
webpack.common.js:
const webpack = require('webpack');
const path = require('path');
const package = require('../package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const autoprefixer = require('autoprefixer');
const build = (() => {
const timestamp = new Date().getTime();
return {
name: package.name,
version: package.version,
timestamp: timestamp,
author: package.author
};
})();
const entry = {
vendor: [
'react',
'react-dom',
'core-js',
'office-ui-fabric-react'
],
app: [
'react-hot-loader/patch',
'./index.tsx',
],
'function-file': '../function-file/function-file.ts'
};
const rules = [
{
test: /\.tsx?$/,
use: [
'react-hot-loader/webpack',
'ts-loader'
],
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
use: {
loader: 'file-loader',
query: {
name: 'assets/[name].[ext]'
}
}
}
];
const output = {
path: path.resolve('dist'),
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
};
const WEBPACK_PLUGINS = [
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.BannerPlugin({ banner: `${build.name} v.${build.version} (${build.timestamp}) © ${build.author}` }),
new webpack.DefinePlugin({
ENVIRONMENT: JSON.stringify({
build: build
})
}),
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer({ browsers: ['Safari >= 8', 'last 2 versions'] }),
],
htmlLoader: {
minimize: true
}
}
})
];
module.exports = {
context: path.resolve('./src'),
entry,
output,
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.scss', '.css', '.html']
},
module: {
rules,
},
optimization: {
splitChunks: {
chunks: 'async',
minChunks: Infinity,
name: 'vendor'
}
},
plugins: [
...WEBPACK_PLUGINS,
new ExtractTextPlugin('[name].[hash].css'),
new HtmlWebpackPlugin({
title: 'letterConfig',
filename: 'index.html',
template: './index.html',
chunks: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
title: 'letterConfig',
filename: 'function-file/function-file.html',
template: '../function-file/function-file.html',
chunks: ['function-file']
}),
new CopyWebpackPlugin([
{
from: '../assets',
ignore: ['*.scss'],
to: 'assets',
}
])
]
};
webpack.prod.js:
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const ENV = process.env.NODE_ENV = process.env.ENV = 'development';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
performance: {
hints: "warning"
},
optimization: {
minimize: true
}
});
index.tsx:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
import App from './components/App';
import './styles.less';
import 'office-ui-fabric-react/dist/css/fabric.min.css';
initializeIcons();
let isOfficeInitialized = false;
const title = 'letterConfig';
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component title={title} isOfficeInitialized={isOfficeInitialized} />
</AppContainer>,
document.getElementById('container')
);
};
/* Render application after Office initializes */
Office.initialize = () => {
console.log('init');
isOfficeInitialized = true;
render(App);
};
/* Initial render showing a progress bar */
render(App);
if ((module as any).hot) {
(module as any).hot.accept('./components/App', () => {
const NextApp = require('./components/App').default;
render(NextApp);
});
}
It turned out that it was looking for a globally defined "React", and so not resolving via an npm module. In the webpack.prod.js file removing the following solved the problem:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},

React. Import svg

Export svg from root file index.ts
import * as upArrow from "./up-arrow.svg";
import * as downArrow from "./down-arrow.svg";
export { upArrow, downArrow };
Try to use in my component
import * as React from "react";
import { Icon } from "components";
import { upArrow, downArrow } from "common/assets";
const CollapseIcon = ({ condition }) => (
<Icon alternative={upArrow} asset={downArrow} condition={condition} />
);
export default CollapseIcon;
If it's important, I use values asset and alternative in src attribute like that
export default styled.img.attrs<IconProps>({
src: ({ condition, alternative, asset }: IconProps) =>
condition ? alternative : asset,
alt: ({ alt }: IconProps) => alt
})`
vertical-align: middle;
`
But get html-element with double quotes. If delete it, element view correctly. Where am I wrong?
I try to declare non-code assets accrding docs, but never change
declare module "*.svg" {
const content: string;
export default content;
}
declare module "*.png";
declare module "*.jpg";
My Webpack config:
"use strict";
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
const getClientEnvironment = require("./env");
const paths = require("./paths");
const publicPath = "/";
const publicUrl = "";
const env = getClientEnvironment(publicUrl);
module.exports = {
devtool: "cheap-module-source-map",
entry: [
require.resolve("./polyfills"),
require.resolve("react-dev-utils/webpackHotDevClient"),
paths.appIndexJs
],
output: {
pathinfo: true,
filename: "static/js/bundle.js",
chunkFilename: "static/js/[name].chunk.js",
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")
},
resolve: {
modules: [paths.appSrc, paths.appNodeModules].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
extensions: [".ts", ".tsx", ".js", ".jsx"],
alias: {
"react-native": "react-native-web"
},
plugins: [
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
]
},
module: {
strictExportPresence: true,
rules: [
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
},
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "static/media/[name].[hash:8].[ext]"
}
},
{
test: /\.(ts|tsx|js|jsx)?$/,
loader: "awesome-typescript-loader",
options: {
getCustomTransformers: require("../config/transformers.js")
}
},
{
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
loader: require.resolve("file-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]"
}
}
]
}
]
},
plugins: [
new InterpolateHtmlPlugin(env.raw),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml
}),
new webpack.NamedModulesPlugin(),
new webpack.DefinePlugin(env.stringified),
new webpack.HotModuleReplacementPlugin(),
new CaseSensitivePathsPlugin(),
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
],
node: {
dgram: "empty",
fs: "empty",
net: "empty",
tls: "empty",
child_process: "empty"
},
performance: {
hints: false
},
externals: {
react: "React",
"react-dom": "ReactDOM"
},
devtool: "source-map"
};
UPD. As I see, browser receive my string as link
Add .svg extension to extenstions list for url-loader like that
...
rules: [
...
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/],
loader: require.resolve("url-loader"),
options: {
limit: 10000,
name: "static/media/[name].[hash:8].[ext]"
}
},
...

Build performance for webpack 2

I need some help in improving my build times. I upgraded my webpack config file to version 2 but the build times are worse compared to before. It takes atleast 3min to complete the build.
Here's my webpack file
/**
* React Starter Kit (https://www.reactstarterkit.com/)
*
* Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
require('dotenv').config();
import path from 'path';
import webpack from 'webpack';
import extend from 'extend';
import AssetsPlugin from 'assets-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import bundles from '../src/bundles'
import merge from 'lodash/merge'
import fs from 'fs'
const DEBUG = !process.argv.includes('--release');
const VERBOSE = process.argv.includes('--verbose');
const GLOBALS = {
'process.env.NODE_ENV': DEBUG ? '"development"' : '"production"',
'process.env.MORTGAGE_CALCULATOR_API': process.env.MORTGAGE_CALCULATOR_API ? `"${process.env.MORTGAGE_CALCULATOR_API}"` : null,
'process.env.API_HOST': process.env.BROWSER_API_HOST ? `"${process.env.BROWSER_API_HOST}"` : process.env.API_HOST ? `"${process.env.API_HOST}"` : null,
'process.env.GOOGLE_ANALYTICS_ID': process.env.GOOGLE_ANALYTICS_ID ? `"${process.env.GOOGLE_ANALYTICS_ID}"` : null,
'process.env.OMNITURE_SUITE_ID': process.env.OMNITURE_SUITE_ID ? `"${process.env.OMNITURE_SUITE_ID}"` : null,
'process.env.COOKIE_DOMAIN': process.env.COOKIE_DOMAIN ? `"${process.env.COOKIE_DOMAIN}"` : null,
'process.env.FEATURE_FLAG_BAZAAR_VOICE': process.env.FEATURE_FLAG_BAZAAR_VOICE ? `"${process.env.FEATURE_FLAG_BAZAAR_VOICE}"` : null,
'process.env.FEATURE_FLAG_REALTIME_RATING': process.env.FEATURE_FLAG_REALTIME_RATING ? `"${process.env.FEATURE_FLAG_REALTIME_RATING}"` : null,
'process.env.SALE_RESULTS_PAGE_FLAG': process.env.SALE_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RESULTS_PAGE_FLAG}"` : null,
'process.env.SALE_RELOADED_RESULTS_PAGE_FLAG': process.env.SALE_RELOADED_RESULTS_PAGE_FLAG ? `"${process.env.SALE_RELOADED_RESULTS_PAGE_FLAG}"` : null,
'process.env.TRACKER_DOMAIN': process.env.TRACKER_DOMAIN ? `"${process.env.TRACKER_DOMAIN}"` : null,
'process.env.USER_SERVICE_ENDPOINT': process.env.USER_SERVICE_ENDPOINT ? `"${process.env.USER_SERVICE_ENDPOINT}"` : null,
__DEV__: DEBUG
};
//
// Common configuration chunk to be used for both
// client-side (client.js) and server-side (server.js) bundles
// -----------------------------------------------------------------------------
const config = {
output: {
publicPath: '/blaze-assets/',
sourcePrefix: ' ',
},
cache: DEBUG,
stats: {
colors: true,
reasons: DEBUG,
hash: VERBOSE,
version: VERBOSE,
timings: true,
chunks: VERBOSE,
chunkModules: VERBOSE,
cached: VERBOSE,
cachedAssets: VERBOSE,
},
plugins: [
new ExtractTextPlugin({
filename: DEBUG ? '[name].css' : '[name].[chunkhash].css',
allChunks: true,
}),
new webpack.LoaderOptionsPlugin({
debug: DEBUG,
})
],
resolve: {
extensions: ['.webpack.js', '.web.js', '.js', '.jsx', '.json'],
modules: [
'./src',
'node_modules',
]
},
module: {
rules: [
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, '../src'),
],
loader: 'babel-loader',
}, {
test: /\.(scss|css)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'sass-loader',
]
})
}, {
test: /\.txt$/,
loader: 'raw-loader',
}, {
test: /\.(otf|png|jpg|jpeg|gif|svg|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?limit=10000',
}, {
test: /\.(eot|ttf|wav|mp3)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
}, {
test: /\.jade$/,
loader: 'jade-loader',
}
],
},
};
//
// Configuration for the client-side bundles
// -----------------------------------------------------------------------------
let clientBundles = {}
Object.keys(bundles).forEach(function (bundle) {
clientBundles[bundle] = [
'bootstrap-loader',
`./src/bundles/${bundle}/index.js`
]
})
merge(
clientBundles,
{
'embedWidget': ['./src/components/Widgets/EmbedWidget/widgetLoader.js']
}
)
const clientConfig = extend(true, {}, config, {
entry: clientBundles,
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: DEBUG ? '[name].js' : '[name].[chunkhash].js',
chunkFilename: DEBUG ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
},
node: {
fs: "empty"
},
// Choose a developer tool to enhance debugging
// http://webpack.github.io/docs/configuration.html#devtool
devtool: DEBUG ? 'cheap-module-source-map' : false,
plugins: [
...config.plugins,
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': true
}),
...(!DEBUG ? [
new webpack.optimize.UglifyJsPlugin({
compress: {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
screw_ie8: true,
// jscs:enable requireCamelCaseOrUpperCaseIdentifiers
warnings: VERBOSE,
},
}),
new webpack.optimize.AggressiveMergingPlugin(),
] : []),
new AssetsPlugin({
path: path.join(__dirname, '../build'),
filename: 'assets.js',
processOutput: x => `module.exports = ${JSON.stringify(x)};`,
}),
],
});
//
// Configuration for the server-side bundle (server.js)
// -----------------------------------------------------------------------------
var srcDirs = {};
fs.readdirSync('src').forEach(function(path) {
srcDirs[path] = true
});
function isExternalFile(context, request, callback) {
var isExternal = request.match(/^[#a-z][a-z\/\.\-0-9]*$/i) && !srcDirs[request.split("/")[0]]
callback(null, Boolean(isExternal));
}
const serverConfig = extend(true, {}, config, {
entry: './src/server.js',
output: {
path: path.join(__dirname, '../build/public/blaze-assets/'),
filename: '../../server.js',
libraryTarget: 'commonjs2',
},
target: 'node',
externals: [
/^\.\/assets$/,
isExternalFile
],
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false,
},
devtool: DEBUG ? 'cheap-module-source-map' : 'source-map',
plugins: [
...config.plugins,
new webpack.DefinePlugin({
...GLOBALS,
'process.env.BROWSER': false,
'process.env.API_HOST': process.env.API_HOST ? `"${process.env.API_HOST}"` : null
}),
new webpack.NormalModuleReplacementPlugin(/\.(scss|css|eot|ttf|woff|woff2)$/, 'node-noop'),
new webpack.BannerPlugin({
banner: `require('dotenv').config(); require('newrelic'); require('source-map-support').install();`,
raw: true,
entryOnly: false
})
],
});
export default [clientConfig, serverConfig];
here's my start.js file
/**
* React Starter Kit (https://www.reactstarterkit.com/)
*
* Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
import Browsersync from 'browser-sync'
import webpack from 'webpack'
import webpackMiddleware from 'webpack-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import run from './run'
import runServer from './runServer'
import webpackConfig from './webpack.config'
import clean from './clean'
import copy from './copy'
const DEBUG = !process.argv.includes('--release')
/**
* Launches a development web server with "live reload" functionality -
* synchronizing URLs, interactions and code changes across multiple devices.
*/
async function start () {
await run(clean)
await run(copy.bind(undefined, { watch: true }))
await new Promise((resolve) => {
// Patch the client-side bundle configurations
// to enable Hot Module Replacement (HMR) and React Transform
webpackConfig.filter((x) => x.target !== 'node').forEach((config) => {
/* eslint-disable no-param-reassign */
Object.keys(config.entry).forEach((entryKey) => {
if (!Array.isArray(config.entry[entryKey])) {
config.entry[entryKey] = [config.entry[entryKey]]
}
config.entry[entryKey].unshift('webpack-hot-middleware/client')
})
if (config.output.filename) {
config.output.filename = config.output.filename.replace('[chunkhash]', '[hash]')
}
if (config.output.chunkFilename) {
config.output.chunkFilename = config.output.chunkFilename.replace('[chunkhash]', '[hash]')
}
config.plugins.push(new webpack.HotModuleReplacementPlugin())
config.plugins.push(new webpack.NoEmitOnErrorsPlugin())
config
.module
.rules
.filter((x) => x.loader === 'babel-loader')
.forEach((x) => (x.query = {
...x.query,
// Wraps all React components into arbitrary transforms
// https://github.com/gaearon/babel-plugin-react-transform
plugins: [
...(x.query ? x.query.plugins : []),
['react-transform', {
transforms: [
{
transform: 'react-transform-hmr',
imports: ['react'],
locals: ['module'],
}, {
transform: 'react-transform-catch-errors',
imports: ['react', 'redbox-react'],
},
],
},
],
],
}))
/* eslint-enable no-param-reassign */
})
const bundler = webpack(webpackConfig)
const wpMiddleware = webpackMiddleware(bundler, {
// IMPORTANT: webpack middleware can't access config,
// so we should provide publicPath by ourselves
publicPath: webpackConfig[0].output.publicPath,
// Pretty colored output
stats: webpackConfig[0].stats,
// For other settings see
// https://webpack.github.io/docs/webpack-dev-middleware
})
const hotMiddlewares = bundler
.compilers
.filter((compiler) => compiler.options.target !== 'node')
.map((compiler) => webpackHotMiddleware(compiler))
let handleServerBundleComplete = () => {
runServer((err, host) => {
if (!err) {
const bs = Browsersync.create()
bs.init({
...(DEBUG ? {} : { notify: false, ui: false }),
proxy: {
target: host,
middleware: [wpMiddleware, ...hotMiddlewares],
},
open: false,
// no need to watch '*.js' here, webpack will take care of it for us,
// including full page reloads if HMR won't work
files: ['build/content/**/*.*'],
}, resolve)
handleServerBundleComplete = runServer
}
})
}
bundler.plugin('done', () => handleServerBundleComplete())
})
}
export default start
I am using react-starter-kit but it is slightly modified version of the original. I have tried degrading css-loader to 0.14.5 to see improvements but no difference. Also, removed sourceMap from UglifyJSPlugin. Any help is appreaciated!

Resources