What's the best way to create a development build bundle for a create-react-app project?
npm run build builds something that's buggy in ways that development isn't and it ignores my NODE_ENV setting
This works for the react-scripts 5.0.1:
Install react-app-rewired: npm install react-app-rewired
Replace the build script in your package.json:
"scripts": {
"build": "react-app-rewired build"
}
Create the config-overrides.js file in the root folder of your project.
module.exports = function override(config) {
config.mode = 'development';
config.optimization.minimize = false;
return config;
};
If you want to create two build modes you can add a condition that checks the mode.
"scripts": {
"build": "cross-env react-app-rewired build",
"build:dev": "cross-env DEV_MODE=true react-app-rewired build",
}
module.exports = function override(config) {
if (process.env.DEV_MODE) {
config.mode = 'development';
config.optimization.minimize = false;
}
return config;
};
I have a React.js application which is part of an oAuth2 architecture and in order to get a token it uses Authorization Code + PKCE flow as explained here.
The issue is that when I try to run the app npm start I get the following error:
I tried with the suggested approach at many places to add the following in my package.json after devDependencies:
"browser": {
"crypto": false
}
but I still have the same issue. I am using Node.js 14.0.0
This is a big issue, and I will not elaborate on the various moving parts that causes the issue.
What you need to do:
run yarn add crypto-browserify stream-browserify buffer
run yarn add -D react-app-rewired
create a new file in the root of your directory: config-overrides.js with the contents:
const webpack = require('webpack');
module.exports = function override(config, env) {
config.resolve = config.resolve || {};
config.resolve.fallback = config.resolve.fallback || {};
config.resolve.fallback.crypto = require.resolve('crypto-browserify');
config.resolve.fallback.stream = require.resolve('stream-browserify');
config.resolve.fallback.buffer = require.resolve("buffer")
config.resolve.alias.buffer = 'buffer'
config.plugins = config.plugins || [];
config.plugins.push(new webpack.ProvidePlugin({Buffer: ['buffer', 'Buffer'],}));
return config;
}
Edit your package.json and make sure it looks like this:
...
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
...
run rm -fr node_modules/.cache
Read more:
The actual issue that caused all this - "BREAKING CHANGE: webpack < 5 used to include polyfills..." - issue 11756 at CRA
related react-oauth2-pkce issue 33
How to re-wire CRA
I created .env and .env.production files with differnt values:
API=http://localhost:8082/api/
Created config:
var config = {};
config.api = process.env.API;
module.exports = config;
I try to get config in an action but 'api' field is undefined.
const config = require("../config/server");
console.log(config);
I think need to add something in the start command
"start": "webpack-dev-server --mode development --inline --progress",
"production": "webpack-dev-server --mode production --inline --progress",
But I'm not sure what I should add here.
Could you please help me?
Create a webpack.config.js in project root directory and add entries in it as suggested below
webpack.config.js
// Do the imports at top of config file
var webpack = require('webpack');
var dotenv = require('dotenv').config({path: __dirname + '/.env'});
....
// In plugins section
plugins: [
...
new webpack.DefinePlugin({ "process.env": dotenv.parsed })
]
Don't forget to install dotenv package. (yarn add dotenv / npm install --save dotenv).
Then change start script as below
"start": "webpack"
To get more insights on webpack.config.js options, check here.
(or)
Edit
If you have only fewer environment variables and don't want to maintain them in a separate file (.env/.env.production) and load it via webpack DefinePlugin, libraries like cross-env will come handy here.
Change your scripts as below
"start": "cross-env API=http://localhost:8082/api webpack-dev-server --mode development --inline --progress",
"production": "cross-env API=https://prodsite.com/api webpack-dev-server --mode production --inline --progress",
You can add multiple variables too
"start": "cross-env NODE_ENV=... API=... webpack-dev-server ..."
I use the dotenv package along with the define plugin.
We first parse the .env file then iterate over the values assembling an object. Afterwards we pass the assembled object to the define plugin. The define plugin sets them as global constants accessible from your application.
const dotenv = require('dotenv');
const fileEnv = dotenv.config({ path: '.env.development' }).parsed;
const envKeys = Object.keys(fileEnv).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(fileEnv[next]);
return prev;
}, {});
module.exports = {
...
plugins: [
new webpack.DefinePlugin(envKeys),
],
...
}
I have an app that is using express to serve some API calls and webpack to server some static assets in development. All works ok until I bring nodemon into the setup.
When I run my app using nodemon I get this error when I update a file on the React side (client side).
GET http://localhost:3004/__webpack_hmr
net::ERR_INCOMPLETE_CHUNKED_ENCODING
If I run my app without nodemon the HMR works perfect and all updates and reloads the React modules.
In my package.json file I have the following scripts.
The start one works fine but the dev one using nodemon causes the error when a change is made to a React component.
"scripts": {
"start": "node server/app.js",
"dev": "nodemon server/app.js",
"build": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.prod.config.js --progress --colors",
"heroku-postbuild": "npm run build"
},
Any ideas what might be causing this?
Here is my entry point in my webpack config file.
entry: [
`${ROOT_DIR}/js/index`,
'webpack-hot-middleware/client'
]
Here is my webpack setup with webpackHotMiddleware which I call in my app.js and pass in App to it. App being my express server.
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('../../webpack/webpack.dev.config.js');
const compiler = webpack(webpackConfig);
const devOptions = {
noInfo: true,
publicPath: webpackConfig.output.publicPath,
stats: { colors: true },
historyApiFallback: true
}
// WEBPACK MIDDLEEWARE
function setupWebpackMiddleware( app ){
app.use(webpackHotMiddleware(compiler));
app.use(webpackDevMiddleware(compiler, devOptions));
}
module.exports = {setupWebpackMiddleware}
Using the following versions.
"webpack": "^2.2.1"
"webpack-dev-middleware": "^1.10.0",
"webpack-dev-server": "~1.16.2",
"webpack-hot-middleware": "^2.16.1"
I'd like to know if it's possible using react-script to rename src to something else like app folder
You can use react-app-rewired to override react paths configuration.
In my case, I can change the paths in config-overrides.js file
const path = require('path');
module.exports = {
paths: function (paths, env) {
paths.appIndexJs = path.resolve(__dirname, 'mysrc/client.js');
paths.appSrc = path.resolve(__dirname, 'mysrc');
return paths;
},
}
Not sure if this answers your question but I'll give it a shot. My directory structure looks like this:
/root
--/app
----/build
----/public
------index.html
----/src
------index.js
app.js
package.js
My /root/package.json has this in it:
"scripts": {
"build": "cd app && npm run build",
"start": "node app.js",
"serve": "cd app && npm run start"
},
"dependencies": {
"express": "^4.8.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "^1.0.17"
},
and my /root/app/package.json looks like this:
"scripts": {
"build": "react-scripts build",
"start": "set PORT=3000 && react-scripts start"
},
"dependencies": {
"react-scripts": "^1.0.17"
}
To run the development version of Reactjs, in the /root I can just npm run serve to serve up the dev version.
I am using node and express, so to run the production version of Reactjs,
in the /root I can just npm run build to create the /root/app/build directory. I have a router that looks like this:
var options = {root : config.siteRoot + '/app/build'};
mainRte.all('/', function(req, res) {
console.log('In mainRte.all Root');
res.sendFile('index.html', options);
});
so when I run /root/app.js in node and surf to "/" it opens up /root/app/public/index.html and then /root/app/index.js.
Hopefully that helps.
react-app-rewired allows for this exact customization.
1
Install react-app-rewired as a dev dependency:
npm install --save-dev react-app-rewired
2
In package.json, change these lines
"scripts": {
"react-start": "react-scripts start",
"react-build": "react-scripts build",
"react-test": "react-scripts test",
...
}
to
"scripts": {
"react-start": "react-app-rewired start",
"react-build": "react-app-rewired build",
"react-test": "react-app-rewired test",
...
}
3
Create a config-overrides.json file in your project directory root with the following contents:
const paths = require('react-scripts/config/paths')
const path = require('path')
// Make the "app" folder be treated as the "src" folder
paths.appSrc = path.resolve(__dirname, 'app')
// Tell the app that "src/index.js" has moved to "app/index.js"
paths.appIndexJs = path.resolve(__dirname, 'app/index.js')
Now your app folder is the new src!
You can also customize many other things, such as the name of the "public" folder:
paths.appPublic = path.resolve(__dirname, 'subfolder/public')
paths.appHtml = path.resolve(__dirname, 'subfolder/public/index.html')
And you can also change the location of package.json and node_modules. See here for the full list.
I know this is an old question but I'm still gonna post my solution since it might help someone.
I got it working by doing the following:
Run npm run eject. This exposes some internal configuration stuff from create-react-app
Open your package.json and edit the respective regexes under jest.collectCoverageFrom and jest.testMatch to match your test path
Alter the paths for appSrc, appIndexJs and testsSetup in the config/paths.js file
T0astBread's answer is nearly perfect, but there's an additional reference to "src" that he missed inside modules.js.
Specifically:
return {
src: paths.appSrc,
};
needs to be changed to
return {
newSrcName: paths.appSrc,
};
This is a great question and a valid scenario for changing this folder name is when migrating old react projects to CRA.
Here's another approach I found that breaks less things:
Create a symlink with:
ls -s ./app src
Then add this in config-overrides.js, to allow webpack to process the symlink:
module.exports = (config, ...rest) => {
return { ...config, resolve: { ...config.resolve, symlinks: false } };
};
Then install react-app-rewired and add this to your package.json:
"start": "react-app-rewired start",
While Cong Dan Luong's answer is correct as far as renaming the folder goes, it will break testing with jest. You need to expand the config-overrides.js module.exports part with the following:
module.exports = {
jest: function(config) {
config.collectCoverageFrom = ['client/**/*.{js,jsx,ts,tsx}', '!client/**/*.d.ts'];
config.testMatch = [
'<rootDir>/client/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/client/**/*.{spec,test}.{js,jsx,ts,tsx}',
];
config.roots = ['<rootDir>/client'];
return config;
},
// The paths config
paths: function(paths, env) {
paths.appIndexJs = path.resolve(__dirname, 'client/index.js');
paths.appSrc = path.resolve(__dirname, 'client');
return paths;
},
};
In my above example I am using 'client' instead of 'src'. npm test now works.
Perhaps a symbolic link might address your reasons for wanting to do this:
ln -s ./src/ ./app
The src folder will remain but you can work with it as if it was the app folder.
If, like me you're using vscode you can also do:
Cmd-shift-p search workspace settings, and add the following:
{
"files.exclude": {
"src/": true
}
}
You could do similarly with other editors
Create file in root of your project, insert this code and run.
const fs = require('fs');
const path = './node_modules/react-scripts/config/paths.js';
const folder = 'app';
fs.readFile(path, 'utf8', (err, data) => {
if (err) throw err;
data = data.replace(/src/g, folder);
fs.writeFile(path, data, 'utf8');
});