Related
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.
I have a basic react app with Craco for tailwindcss support. What I'm trying to do is read from a main.mjs file but when I try to import the file, I run into a ts2307 error that module cannot be found. Is there a way for me to get app to find *.mjs files? Like I have tried going through the craco config documentation but I keep missing the point I guess.
in your craco.config.js file, need to allow tailwind css as well as overwrite webpack configuration as below.
module.exports = {
style: {
postcss: {
plugins: [require('tailwindcss'), require('autoprefixer')],
},
},
webpack: {
configure: {
module: {
rules: [
{
type: 'javascript/auto',
test: /\.mjs$/,
use: [],
},
],
},
},
},
};
I'm trying to obfuscate my tailwindcss class names when someone views my html. A medium article suggested doing so via webpack. Since I'm using Create-React-App, I want to add the code below to webpack via the CRACO (Create React App Configuration Override) config file. How do I add this code in craco.config.js?
The code I'd like to edit in webpack.config.js:
// webpack.config.js / loaders section
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[sha1:hash:hex:4]'
}
}
]
}
As per this article
My current craco config file:
// craco.config.js
module.exports = {
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
},
},
webpack: {
// somehow that code above fits in here...
}
}
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.
I'm trying to build a lerna package with a create-react-app package and a simple component library. My component is as follows:
import React, { Component } from "react";
import PropTypes from "prop-types";
class Layout extends Component {
render = () => {
let style = {
fontSize: 14,
fontFamily:
"-apple-system, system-ui, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif",
fontWeight: 400
};
return <div style={style}>{this.props.children}</div>;
};
}
export default Layout;
And my original create-react-app is as follows:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/app/App/App';
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import React, { Component } from "react";
import Layout from "#project/webux/lib/Layout";
class App extends Component {
render = () => {
return (
<Layout>
Hello!
</Layout>
);
};
}
export default App;
When running, I'm getting the following error:
../webux/lib/Layout/index.js
SyntaxError: /Volumes/workspace/dev/packages/webux/lib/Layout/index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (5:12):
3 |
4 | class Layout extends Component {
> 5 | render = () => {
| ^
6 | let style = {
7 | fontSize: 14,
8 | fontFamily:
Add #babel/plugin-proposal-class-properties (https://git.io/vb4SL) to the 'plugins' section of your Babel config to enable transformation.
This error happens because create-react-app does not transpile files outside its project. As my component Layout resides in another lerna package in another directory, it is not transpiled.
In order to solve it, I've ejected my create-react-app application and end up with the following webpack configuration file, where I've added the ====INCLUDED=== piece of code to set the input directories (I've added the directory immediately above the project, as this will point to my lerna \packages directory, so all packages files are processed:
...
resolve: {
// 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/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
// 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/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Allows for better profiling with ReactDevTools
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// 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]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
enforce: 'pre',
use: [
{
options: {
cache: true,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
resolvePluginsRelativeTo: __dirname,
},
loader: require.resolve('eslint-loader'),
},
],
//=================== INCLUDED =====================/
//
// Included the lenrna packages directory (up directory)
// in order to transpile all files from other packages.
//
//===================================================
include: [path.resolve(__dirname, "../.."), 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: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
/// Renato Mendes
/// This was added to support transpiling of monorepo modules.
/// See https://github.com/webpack/webpack/issues/6799
///
/// Original:
/// include: paths.appSrc
///
include: [path.resolve(__dirname, "../.."), path.resolve(paths.lernaRoot + "/packages"), paths.appSrc],
// include: paths.appSrc,
include: [paths.lernaRoot, paths.appSrc],
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
],
// 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,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
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,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "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 MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "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 doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
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.
],
},
],
}
...
I'm still getting the error, as my external component is not getting transpiled.
How to make the above webpack config transpile my code that resides in another package of my lerna project? Any other config missing? What am I doing wrong?
The bad news: This is a common problem. Create React App doesn't support monorepos, as of ~3.2.0 / late 2019. If you want to share components between lerna sibling packages, many people either avoid using "CRApp", or, include a build script in their component library packages and commit and export pre-transpiled ES5 files.
The good news: I found a fix that seems to work, and doesn't require ejecting CRA. Tested with both local build and test deploy to github pages.
It uses craco, which provides an API for editing CRA's webpack config without ejecting. Craco has plugins which add webpack loaders etc; we'll need craco-babel-loader:
npm i --save #craco/craco craco-babel-loader
...then there are some further CRACO setup steps, check https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#installation for the latest. At time of writing, you need to replace the following CRA scripts in package.json:
react-scripts start => craco start
react-scripts build => craco build
react-scripts test => craco test
Then we need to create a config file, craco.config.js, in the root of the CRA/craco package that receives ES6+ JSX components from sibling packages, and we need to list the package names to send to babel:
// crago.config.js
// see: https://github.com/sharegate/craco
const path = require('path')
const fs = require('fs')
const cracoBabelLoader = require('craco-babel-loader')
// Handle relative paths to sibling packages
const appDirectory = fs.realpathSync(process.cwd())
const resolvePackage = relativePath => path.resolve(appDirectory, relativePath)
module.exports = {
plugins: [
{
plugin: cracoBabelLoader,
options: {
includes: [
// No "unexpected token" error importing components from these lerna siblings:
resolvePackage('../some-component-library'),
resolvePackage('../more-components'),
resolvePackage('../another-components-package'),
],
},
},
],
}