Every time we access our deployed ReactJS App, we get a red square for the dev-tools, saying we're not on an optimized build for production.
The app is a SingleSPA Microfrontends web app.
The fact is that every microfrontend and the root orchestrator get built in production mode.
Follows a configuration for a single microfrontend, if you need other stuff please ask me, since I'm a little newbie with singleSPA, maybe I'm forgotting to put something
This is the command that Jenkins runs when it deploys:
"build:prod": "NODE_ENV=production webpack --mode=production --config config/webpack.config.prod.js",
This is out webpack.config.prod.js
require('./env');
const { merge } = require('webpack-merge');
const autoprefixer = require('autoprefixer');
const PostcssFlexbugsFixes = require('postcss-flexbugs-fixes');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const path = require('path');
const webpack = require('webpack');
const common = require('./webpack.common.js');
const paths = require('./paths');
// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = (a, b) => merge(common(a, b), {
mode: 'production',
devtool: 'source-map',
resolve: {
fallback: {
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
dgram: false,
fs: false,
net: false,
tls: false,
// eslint-disable-next-line camelcase
child_process: false,
},
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'],
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
strictExportPresence: true,
rules: [
{
test: /node_module\/dagre\/dist\/dagre.core.js/,
use: [
'imports?this=>window',
'script',
],
},
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
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)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
postcssOptions: {
plugins: () => [
PostcssFlexbugsFixes,
autoprefixer({
overrideBrowserslist: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
},
],
},
{
test: /\.less$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
url: false,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
postcssOptions: {
ident: 'postcss',
plugins: () => [
PostcssFlexbugsFixes,
autoprefixer({
overrideBrowserslist: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
relativeUrls: true,
javascriptEnabled: true,
paths: [path.resolve(__dirname, 'node_modules')],
},
},
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: '#svgr/webpack',
options: {
babel: false,
icon: true,
},
},
],
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader don't uses a "test" so it will catch all modules
// that fall through the other loaders.
{
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
// Turn off performance hints during development because we don't do any
// splitting or minification in interest of speed. These warnings become
// cumbersome.
performance: {
hints: false,
},
});
This is our webpack.common.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { merge } = require('webpack-merge');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const path = require('path');
const singleSpaDefaults = require('webpack-config-single-spa-react');
const webpack = require('webpack');
const dotenv = require('dotenv')
.config({ path: `.env.${process.env.NODE_ENV}` });
module.exports = (webpackConfigEnv, argv) => {
const defaultConfig = singleSpaDefaults({
orgName: 'xxx',
projectName: 'yyy',
webpackConfigEnv,
argv,
});
return merge(defaultConfig, {
module: {
rules: {
test: /\.(bmp|png|jpg|jpeg|gif|webp)$/i,
exclude: /node_modules/,
type: 'asset/resource',
},
},
resolve: {
fallback: {
https: false,
http: false,
},
alias: {
'#Api': path.resolve(__dirname, '../src/api/'),
'#Components': path.resolve(__dirname, '../src/components/'),
'#Container': path.resolve(__dirname, '../src/container/'),
'#Img': path.resolve(__dirname, '../src/resources/images/'),
'#Helpers': path.resolve(__dirname, '../src/helpers/'),
'#Src': path.resolve(__dirname, '../src/'),
'#State': path.resolve(__dirname, '../src/store/state/'),
'#Store': path.resolve(__dirname, '../src/store/'),
'react-dom': '#hot-loader/react-dom',
},
},
plugins: [
new CaseSensitivePathsPlugin(),
new CleanWebpackPlugin(),
new webpack.DefinePlugin({
'process.env': JSON.stringify(dotenv.parsed),
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
externals: {
lodash: 'lodash',
moment: 'moment',
react: 'react',
'react-dom': 'react-dom',
},
});
};
This is our env.js
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const paths = require('./paths');
// Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve('./paths')];
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
);
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.mock`,
`${paths.dotenv}.${NODE_ENV}.development`,
`${paths.dotenv}.${NODE_ENV}.production`,
//`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
paths.dotenv,
].filter(Boolean);
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set.
// https://github.com/motdotla/dotenv
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
});
// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebookincubator/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter((folder) => folder && !path.isAbsolute(folder))
.map((folder) => path.resolve(appDirectory, folder))
.join(path.delimiter);
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
// .filter((key) => REACT_APP.test(key))
.reduce((env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether we’re running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl,
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { raw, stringified };
}
module.exports = getClientEnvironment;
This doesn't seem to be a configuration issue with the build or configuration.
It sounds like you might be using the importmap overrides library that comes with single-spa.
Using the dev-libs property when using this library like causes it do use development bundles.
<import-map-overrides-full
show-when-local-storage="devtools"
dev-libs
></import-map-overrides-full>
The dev-libs attribute indicates that you prefer using development versions of third party libraries
like react when the import-map-overrides ui is active. The presence of that attribute turns on this feature.
For example, if you have react in your import map pointing to https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js
the dev-libs attribute will automatically override it to https://cdn.jsdelivr.net/npm/react/umd/react.development.js.
You can also turn this feature on/off via localStorage. localStorage.setItem('import-map-overrides-dev-libs', false) will
forcibly turn this feature off, while calling it with true will turn it on.
Is it possible to use Webpack Hot Module Replacement (HMR) with a hoisted Lerna React project?
This is because each Lerna React package (see below as an example) is built independently and when a webpack-dev-server is launched on the main project(p3 for instance), it can only see its own changes (I mean p3 changes only) and NOT its dependencies (p1 or p2) changes sitting in other packages of its monorepo
some_lerna_project
/node_modules
/packages
/p1
/src
package.json
/p2
/src
package.json
/p3
/src
package.json
webpack.confing.js
lenra.json
If yes, would you please provide a sample config?
You need these packages be part of transpiled files.
// webpack.config.js
const path = require('path');
const PATH_DELIMITER = "[\\\\/]"; // match 2 antislashes or one slash
const safePath = (module) => module.split(/[\\\/]/g).join(PATH_DELIMITER);
const generateIncludes = (modules) => {
return [
new RegExp(`(${modules.map(safePath).join("|")})$`),
new RegExp(
`(${modules.map(safePath).join("|")})${PATH_DELIMITER}(?!.*node_modules)`
),
];
};
const transpileModules = ["#my-scope/p1", "#my-scope/p2"]; // using scoped packages
module.exports = {
mode: "development",
entry: {
app: "./src/index.js",
print: "./src/print.js",
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.m?js$/,
include: generateIncludes(transpileModules),
use: {
loader: "babel-loader",
options: {
// use your preferred babel config
presets: ["#babel/preset-env"],
},
},
},
],
},
resolve: {
symlinks: false, // Avoid Webpack to resolve transpiled modules path to their real path
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
};
This code is based on next-transpile-modules which enables Next.js projects to transpile mono-repo packages.
Im a new beginner on develop react app.
Im trying to figure out how to set up my webpack.config.js file.
I have following ended up with this structure as you can see on the picture link below.
My question is: When im running 'npm run build' , its hashing the picture and put it into the /dist folder. How can i configure so it does not?
Because im using copyWebpackPlugin() to copy my images and push it to the dist folder, but i dont want the picture which i marked with arrow.
If anyone have some advice just bring it on.
This is how my webpack.config.js file look like:
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader"
},
{
test: /\.s?css$/,
loader: ["style-loader", "css-loader"]
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
loader: "url-loader?limit=100000"
}
]
},
resolve: { extensions: [".js", ".jsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
filename: "bundle.js"
},
devtool: "cheap-module-eval-source-map",
devServer: {
contentBase: path.join(__dirname, "public/"),
proxy: {
"/api/*": {
target: "http://localhost:3000/",
secure: "true"
}
},
port: 4000,
publicPath: "http://localhost:4000/dist/",
hotOnly: true,
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(["dist"]),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./public/index.html"
}),
new CopyWebpackPlugin([{ from: "public/images", to: "images" }])
]
};
I would suggest instead of copy-webpack-plugin use file-loader to copy images
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [{
loader: 'file-loader',
options: {
name: 'images/[name].[ext]',
}
}]
}
if you want hash instead of name
name: 'images/[hash].[ext]',
Package
npm install --save-dev file-loader
It is because the url-loader has a default fallback to file-loader. So if your image is bigger than the limit you have set for url-loader, it does not rewrite the image to base64 data:image in your css, instead gives it to file-loader and it copies that image to your dist folder (output path).
So if you do not want this, disable the fallback option for url-loader
But I also think you should have configure your webpack to copy the files with file-loader properly instead that copy plugin. But you know...
I would give you an example based on your config but I am currently on mobile so I can't code right now.
Whenever I perform a git pull from my master branch onto my server, all my React files seem to just disappear and the screen turns white.
The temporary workarounds I had found were:
Delete browser cookies, cache & site history, and then close the browser and try again.
Delete node_modules, npm install all react dependencies again
After a while, the site reappears and everything works as normal until the next time after a few pull requests, the problem appears again.
Any console I use on any browser shows no error messages at all.
After 2+ weeks of googling around, I can't seem to find anything that relates to this issue.
Here are my specs:
Ubuntu 16.04 server
Framework: React 16.2.0
webpack 1.12
nginx version: nginx/1.10.3 (Ubuntu)
git version 2.7.4
My webpack settings (for clarity, I compile all my react files with the command):
node_modules/.bin/webpack --config webpack.local.config.js
(local)
var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var config = require('./webpack.base.config.js')
config.devtool = "#eval-source-map"
config.plugins = config.plugins.concat([
new BundleTracker({
filename: './webpack-stats-local.json'
}),
])
config.module.loaders.push({
test: /\.js[x]?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel'],
})
module.exports = config
(base)
var path = require("path")
var webpack = require('webpack')
module.exports = {
context: __dirname,
entry: {
App1: './path/to/App1/',
App2: './path/to/App2/',
// ...
App10: './path/to/App10/',
vendors: ['react'],
},
output: {
path: path.resolve('./backend/static/bundles/local/'),
filename: "[name]-[hash].js"
},
externals: {
"gettext":"gettext",
"django":"django",
}, // add all vendor libs
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
],
module: {
loaders: []
},
resolve: {
modulesDirectories: ['node_modules', 'bower_components'],
extensions: ['', '.js', '.jsx']
},
}
Any help will be greatly appreciated
I solved this problem by updating Webpack to version 4 + updating the dependencies i used while getting rid of the ones i don't use.
I'm currently experimenting with a monorepo architecture.
What I would like to do is in my web package where I run webpack dev server I'd like it to watch certain node_modules (symlinked local packages) for changes and trigger a "rebuild".
This way I'd be able to build dependencies separately and my browser would react to those changes.
My webpack config is the following:
var loaders = require('./../../../build/loaders-default');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: ['./src/index.ts'],
output: {
filename: 'build.js',
path: path.join(__dirname, 'dist')
},
resolve: {
extensions: ['.ts', '.js', '.json']
},
resolveLoader: {
modules: ['node_modules']
},
devtool: 'inline-source-map',
devServer: {
proxy: [
{
context: ['/api-v1/**', '/api-v2/**'],
target: 'https://other-server.example.com',
secure: false
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
inject: 'body',
hash: true
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.jquery': 'jquery'
})
],
module:{
loaders: loaders
}
};
Loaders are just the usual stuff included.
You can config in in webpack.config file or in WebpackDevServer option, to watch for changes also in node_modules (i think that by default webpack watching for changes in all files)
https://webpack.js.org/configuration/watch/#watchoptions-ignored
in the following example webpack ignored all changes in node_modules folder except specific module.
watchOptions: {
ignored: [
/node_modules([\\]+|\/)+(?!some_npm_module_name)/,
/\some_npm_module_name([\\]+|\/)node_modules/
]
}
ignored[0] = Regex to ignore all node_modules that not started with some_npm_module_name
ignored[1] = Regex to ignore all node_modules inside some_npm_module_name
You may also used this link npm linked modules don’t find their dependencies
UPDATE: I'm currently using Next.js 11, and it seems this is no longer necessary.
Granted this question is not regarding Next.js or any specific framework, I'd like to post an answer related to Next.js here since I arrived here from Google as others might also.
Here is what worked for me in my next.config.js:
module.exports = {
// ...
webpackDevMiddleware: config => {
// Don't ignore all node modules.
config.watchOptions.ignored = config.watchOptions.ignored.filter(
ignore => !ignore.toString().includes('node_modules')
);
// Ignore all node modules except those here.
config.watchOptions.ignored = [
...config.watchOptions.ignored,
/node_modules\/(?!#orgname\/.+)/,
/\#orgname\/.+\/node_modules/
];
return config;
},
// ...
}
This targets a specific organization of packages. If you need to just target a specific package:
module.exports = {
// ...
webpackDevMiddleware: config => {
// Don't ignore all node modules.
config.watchOptions.ignored = config.watchOptions.ignored.filter(
ignore => !ignore.toString().includes('node_modules')
);
// Ignore all node modules except those here.
config.watchOptions.ignored = [
...config.watchOptions.ignored,
/node_modules([\\]+|\/)+(?!my-node-module)/,
/\my-node-module([\\]+|\/)node_modules/
];
return config;
},
// ...
}
This builds on Elhay's answer.
snapshot.managedPaths
A common use case for managedPaths would be to exclude some folders from node_modules, e.g. you want webpack to know that files in the node_modules/#azure/msal-browser folder are expected to change, which can be done with a regular expression like the one below:
module.exports = {
snapshot: {
managedPaths: [
/^(.+?[\\/]node_modules[\\/](?!(#azure[\\/]msal-browser))(#.+?[\\/])?.+?)[\\/]/,
],
},
};