Closed jorisre closed 6 years ago
First verify that the config.output.path value is defined.
Then use:
const path = require('path');
config.output.path = path.join(config.output.path, 'sub-build');
That has the benefit that it'll use the right path separators regardless of whether you're on Win/Mac/Linux.
@dawnmist thanks for your reply.
I just tried your solution but it seems not work.
favicon.ico and manifest.json aren't in sub-build
;
screenshot : https://cl.ly/1l1y1b3b0G1m
And i'm getting this error :
Error: ENOENT: no such file or directory, open '/path/to/my/project-react/build/static/js/main.946f3fc2.js'
Thanks for help
Sorry about that.
I have taken a look at the build script in create-react-app, and the problem is that some of the work is being done directly inside the build script itself instead of being done by webpack, so changes to the path inside the webpack configuration will not affect the parts being done outside webpack.
This includes copying the favicon.ico file, and checking the file size for the generated files (where it will be miscalculating the path to the output file, which I think is causing the "no such file" error you are getting).
Take a look at the script here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/build.js
Any place using paths.appBuild
will be referring to the original/default build path rather than the one that you are trying to set.
I don't think that we have the capability to override their config/paths
file. It is the same reason as for why it is difficult to override the "entry" file from src/index.js
to a different filename - the normal rewire process gets bypassed in the build script.
Thanks for your help
Finally, I've add a project-config and clone my project.
Hi Joris, how did you fix it at the end? because i want to change the build directory, but it get the same error as you, did you manage to fix it? cheers
Hi @nahuely,
My aim was to build two products on the same project.
Finally I'm using a .env
file supported by react, If I change an option in my .env then build will be different.
What is the option on .env?
Curious about it too... Trying to simply change the output directory from build
to dist
but to no avail. Since it appears to be a problem in CRA's build script, can we perhaps get @gaearon's eyes on this?
EDIT For full disclosure, this solution proposed by Dan Abramov himself is not valid in my use case, since I need to launch two builds in parallel, each with a different value for a certain env variable, i.e.
FOO=true yarn build
FOO=false yarn build
and my config-overrides.js
is rigged to change the output path conditionally based on that very value, i.e.:
module.exports = function override(config, env) {
if(/^(?:true|1)$/i.test(process.env.FOO)) {
config.output.path = path.resolve(__dirname, 'build-foo');
}
// Otherwise, `build/` is fine
return config;
}
I've managed to achieve this in the past by using a different npm package called patch-package to apply the modification directly to the react-scripts config/paths.js
file. Using patch-package means that the patch is re-applied every time any package is installed/removed/etc, so npm/yarn don't clobber your change.
Because the paths defined in the config/paths.js
file are used in several places, and not all of those places can be modified by react-app-rewired, it is not a problem that react-app-rewired was able to solve. By modifying that file directly, the modification flows through to all the places where the value was being used properly.
For the case of wanting to use multiple alternative build directories, setting the value of the appBuild
variable in config/paths.js
to an environment variable (with a default value if the env variable was not specified) might work (I haven't tested that bit). Note that ensuring that the appBuild
variable is never able to be null, undefined, an empty string, 'src', or 'node_modules' is important - I can see the potential for things to go horribly wrong otherwise (like the first step deleting your project files because it thinks its cleaning the build directory...). If you change it to a dynamic value, be very careful to sanity check that value & protect yourself from a major oops if someone forgot to set it or sets it to a dangerous value! I suspect that potential for error causing serious problems for the user is probably a large part of why create-react-app don't make this a configurable value by default.
Thanks @dawnmist that's extremely helpful, I was not aware of this library but I'll look into it asap. Cheers. :)
I've managed to accomplish this successfully with patch-package by reading appBuild
from an environment variable (which is sanitized beforehand to ensure its value is not among the blacklisted .
, ..
, node_modules
, src
etc.).
Thanks again for your invaluable insight on this. Cheers!
@axedre Can you share with me some code snippets of what worked for you.
I changed the appBuild
path in config/paths.js
in the react-scripts
node module. Created the patch with yarn patch-package react-scripts
then applied the patch with yarn patch-package
. Then tested with yarn build
. Thought that would be enough. Not working still. Outputting to /build
not /dist
.
@jeanpaulangelle-taulia Did you change all 3 instances for appBuild in that file, or only one? If only one - which one?
Patch for react-scripts 3.0.1 should look something like:
diff --git a/node_modules/react-scripts/config/paths.js b/node_modules/react-scripts/config/paths.js
index e5a3e0b..48a5aa8 100644
--- a/node_modules/react-scripts/config/paths.js
+++ b/node_modules/react-scripts/config/paths.js
@@ -77,7 +77,7 @@ const resolveModule = (resolveFn, filePath) => {
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp('dist'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
@@ -100,7 +100,7 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp('dist'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
@@ -135,7 +135,7 @@ if (
module.exports = {
dotenv: resolveOwn('template/.env'),
appPath: resolveApp('.'),
- appBuild: resolveOwn('../../build'),
+ appBuild: resolveOwn('../../dist'),
appPublic: resolveOwn('template/public'),
appHtml: resolveOwn('template/public/index.html'),
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
I think it is the second of the 3 places that is critical for changing the output directory, but changing all 3 makes sure that you've covered them all (and if you do later eject ensures that the ejected config contains your modified path).
@dawnmist Ah gosh. Wow. Thanks, this fixed it. Works now 👍
@jeanpaulangelle-taulia Sorry I only read your comment today. So in fact I've resorted to setting the appBuild folder using an env var, which falls back to 'build'
if undefined or empty; plus I've done a little bit of sanitizing to prevent accidental assignment of "protected" paths, as @dawnmist suggested. Here is my code:
--- a/node_modules/react-scripts/config/paths.js
+++ b/node_modules/react-scripts/config/paths.js
@@ -73,11 +73,31 @@ const resolveModule = (resolveFn, filePath) => {
return resolveFn(`${filePath}.js`);
};
+// Ensure appBuild is 'safe' (i.e. not `src/`, `node_modules/`, './', etc)
+const appBuild = (envAppBuild => {
+ const blacklistedPaths = [
+ '.',
+ '..',
+ '.git',
+ 'node_modules',
+ 'patches',
+ 'public',
+ 'src'
+ ];
+ if(Boolean(envAppBuild) && !blacklistedPaths.includes(envAppBuild)) {
+ return envAppBuild;
+ }
+ return 'build';
+})(process.env.BUILD_DIR);
+
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp(appBuild),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
@@ -100,7 +120,7 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp(appBuild),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
@@ -135,7 +155,7 @@ if (
module.exports = {
dotenv: resolveOwn('template/.env'),
appPath: resolveApp('.'),
- appBuild: resolveOwn('../../build'),
+ appBuild: resolveOwn(`../../${appBuild}`),
appPublic: resolveOwn('template/public'),
appHtml: resolveOwn('template/public/index.html'),
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
HTH :)
Thank you so much!
I have another solution.
rename.js
in your project's root"build": "react-app-rewired build && node rename.js"
It will rename your buildPath dirname after npm run build
rename.js
code:
const fs = require('fs');
const path = require('path');
function removeDir(dir) {
let files = fs.readdirSync(dir);
for (let i = 0; i < files.length; i++) {
let newPath = path.join(dir, files[i]);
let stat = fs.statSync(newPath);
if (stat.isDirectory()) {
removeDir(newPath);
} else {
fs.unlinkSync(newPath);
}
}
fs.rmdirSync(dir);
}
const newRoot = './dist';
if (fs.existsSync(newRoot)) {
removeDir(newRoot);
}
fs.renameSync('./build', newRoot);
Override paths.appBuild
instead. Yourconfig-overrides.js
will become:
const path = require('path');
module.exports = {
paths: function (paths, env) {
paths.appBuild = path.join(paths.appBuild, 'sub-build');
return paths;
},
}
You can also use an environment variable, e.g. like this:
const path = require('path');
module.exports = {
paths: function (paths, env) {
paths.appBuild = path.join(paths.appBuild, process.env.BUILD_PATH || 'build');
return paths;
},
};
My config-overrides.js file looks like this:
module.exports = function override(config, env) {
// support mono repo folder outside of `/src`
aliasDangerous({...configPaths('tsconfig.paths.json'),})(config)
return paths
};
I tried modifying it like this
module.exports = function override(config, env) {
// support mono repo folder outside of `/src`
aliasDangerous({...configPaths('tsconfig.paths.json'),})(config)
// inspired by the code in the previous comments (but doesnt work)
config.paths = (paths, env) => {
paths.appBuild = path.join(paths.appBuild, 'sub-build');
return paths
}
return paths
};
and I get the following error:
Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration has an unknown property 'paths'. These properties are valid:
object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }
For typos: please correct them.
For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
Loaders should be updated to allow passing options via loader options in module.rules.
Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
plugins: [
new webpack.LoaderOptionsPlugin({
// test: /\.xxx$/, // may apply this only for some modules
options: {
paths: …
}
})
]
Using this export function style above (instead of exporting an object), how can I change the build output path of webpack?
@cliffordfajardo You cannot - it is not possible to properly modify the output path through the webpack config due to the way that create-react-app's paths are set.
The export function style only modifies the webpack config. In order to modify the output paths properly, there are additional places that have to be modified - it can't just be done by the webpack config.
You will need to migrate to the object export format in order to modify the output path. Migrating is actually very simple - the function that you currently output in the export function style just gets assigned to the 'webpack' field of the object. Then you create the path field as a new function that modifies and returns the paths.
Add in your .env-cmdrc
file variable:
"BUILD_PATH": "./newpatch",
For anyone who is still struggling with this, I'm guessing that you're using create-react-app which offers a solution: https://stackoverflow.com/questions/41495658/use-custom-build-output-folder-when-using-create-react-app
// package.json
"scripts": {
...
"build": "BUILD_PATH='./site' react-app-rewired build"
},
Hi !
What's the best way to change the build output path ? I'm trying to modify the
config.output.path
var but i'm getting this error :Some code :
My aim is to generate multiples builds.
Thanks for help