Next.js vert slow to start on Windows OS - reactjs

I have this big issue with starting Next.js, the same project on 2 different OS, it has completely different startup times, 1s on MacOSX vs 10s on Windows 11.
Has anyone else had the same problem? Is there any way to fix it?
enter image description here
Here's my next.js conf:
const { resolve } = require('path')
const withBundleAnalyzer = require('#next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
openAnalyzer: true,
})
module.exports = withBundleAnalyzer(
{
i18n: {
locales: ['en', 'it'],
defaultLocale: 'en',
localeDetection: true,
},
compiler: {
removeConsole: process.env.NODE_ENV !== 'development',
},
swcMinify: true,
eslint: {
ignoreDuringBuilds: true,
},
webpack: (config) => {
config.plugins = config.plugins || []
config.optimization.providedExports = true
config.resolve.alias = {
...config.resolve.alias,
'#': resolve(__dirname, './src/'),
}
config.resolve.fallback = { ...config.resolve.fallback, fs: false }
config.module.rules.push({
test: /\.(glsl|vs|fs|vert|frag)$/,
use: ['raw-loader', 'glslify-loader'],
})
return config
},
},
)

Related

How to make www.mydomain.com work in nextjs

I am working on a project and trying to make www.mydomain.com work. I am working on localhost:3000 and when I put in url: mydomain.com:3000 it works but when I set www.mydomian.com:3000 it redirects me on mydomain.is which is my main page.
this is my code in next.config.js file
const path = require("path");
const redirects = require("./redirects");
/** #type {import('next').NextConfig} */
const nextConfig = async () => {
return {
reactStrictMode: false,
swcMinify: true,
sassOptions: {
includePaths: [path.join(__dirname, "src/styles")],
},
redirects() {
return Promise.resolve(redirects);
},
images: {
domains: ["mydomain.cdn.prismic.io", "images.prismic.io"],
},
i18n: {
// https://nextjs.org/docs/advanced-features/i18n-routing
localeDetection: false,
locales: ["is", "en"],
defaultLocale: "is",
domains: [
{
domain: 'mydomain.is',
defaultLocale: 'is',
},
{
domain: 'mydomain.com',
defaultLocale: 'en',
},
],
},
};
};
module.exports = nextConfig;`

Next.js Module parse failed: Unexpected character '�' (1:0)

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
I tried multiple approaches on the web corresponding the issue but nothing seemed to work.
I have not been able to find a solution to this problem.
This is what my next.config.js file looks like
const withPlugins = require('next-compose-plugins')
const withImages = require('next-images')
const CompressionPlugin = require('compression-webpack-plugin')
const withBundleAnalyzer = require('#next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
let prefixUrl = ''
let imgLoaderUrl = ''
switch (process.env.ENV) {
case 'production':
prefixUrl = 'https://webstatic.circleslive.com'
imgLoaderUrl = 'https://webstatic.circleslive.com'
break
case 'staging':
prefixUrl = 'https://staging-webstatic.circleslive.com'
imgLoaderUrl = 'https://staging-webstatic.circleslive.com'
break
case 'development':
prefixUrl = ''
imgLoaderUrl = '/_next/image'
break
default:
prefixUrl = ''
imgLoaderUrl = '/_next/image'
break
}
const nextConfig = {
poweredByHeader: false,
trailingSlash: false,
compress: true,
future: {
webpack5: true,
},
env: {
ENV: process.env.ENV,
},
serverRuntimeConfig: {
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
},
images: {
loader: 'default',
path: imgLoaderUrl,
},
webpack: (config) => {
config.optimization.minimize = true
config.plugins.push(
new CompressionPlugin({
test: /\.js$|\.css$|\.html$/,
}),
)
return config
},
build: {
extend(config) {
config.module.rules.push({
options: {
fix: true,
},
})
},
},
}
module.exports = withPlugins(
[
[
withImages,
{
assetPrefix: prefixUrl,
dynamicAssetPrefix: true,
inlineImageLimit: false,
},
],
[withBundleAnalyzer],
],
nextConfig,
)
Try the following:
module.exports = withPlugins(
[
[
{
assetPrefix: prefixUrl,
dynamicAssetPrefix: true,
inlineImageLimit: false,
},
],
[withBundleAnalyzer],
],
withImages(nextConfig)
)

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.

next.js bundle analyzer isn't creating pages to vew bundles

I'm trying to reduce the bundle size of my site by using https://www.npmjs.com/package/#next/bundle-analyzer
At the moment, when I run npm analyze with
"analyze": "cross-env ANALYZE=true next build",
It isn't outputting the html files with the visualization needed.
This is my next.config.js
const withPWA = require('next-pwa')
const runtimeCaching = require('next-pwa/cache')
const withBundleAnalyzer = require('#next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withPWA({
pwa: {
dest: 'public',
runtimeCaching,
},
poweredByHeader: false,
},
withBundleAnalyzer(),
)
using this nextjs-analyze-app-bundle tutorial.
What is going wrong?
Looks like this has been answered on Vercel's issues board. Copying their solution here:
These plugins are functions that enhance the configuration object, so
you have to wrap them instead of providing them as arguments:
const withPWA = require('next-pwa')
const runtimeCaching = require('next-pwa/cache')
const withBundleAnalyzer = require('#next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer(withPWA({
pwa: {
dest: 'public',
runtimeCaching,
},
poweredByHeader: false,
}));
Before I was doing it like this,
module.exports = withBundleAnalyzer(
withPWA({
pwa: {
dest: 'public',
runtimeCaching,
},
poweredByHeader: false,
})
)
module.exports =
{
env: {
BASE_URL: process.env.BASE_URL,
},
future: {
webpack5: true,
},
reactStrictMode: true,
}
Not sure but I think you should only need to have one module.exports so I wrapped my other stuff inside the withBundleAnalyzer like this
module.exports = withBundleAnalyzer(
withPWA({
pwa: {
dest: 'public',
runtimeCaching,
},
poweredByHeader: false,
}),
{
env: {
BASE_URL: process.env.BASE_URL,
},
future: {
webpack5: true,
},
reactStrictMode: true,
}
)

Can't get React Hot Loader to work

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.

Resources