How to configure react-script so that it doesn't override tsconfig.json on 'start'

I'm currently using create-react-app to bootstrap one of my projects. Basically, I'm trying to set up paths in tsconfig.json by adding these to the default tsconfig.json generated by create-react-app:

"baseUrl": "./src",
"paths": {
"interfaces/*": [
"common/interfaces/*",
],
"components/*": [
"common/components/*",
],
},

However, every time I run yarn start which basically runs react-scripts start, it deletes my changes and generates the default configurations again.

How can I tell create-react-app to use my custom configs?

46819 次浏览

I was able to do this by using advice from this issue.

Put the configuration options react scripts likes to remove in a separate file (e.g. paths.json) and reference it from tsconfig.json via the extends directive.

paths.json:

{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"interfaces/*": [ "common/interfaces/*"],
"components/*": [ "common/components/*"],
}
}
}

tsconfig.json

{
"extends": "./paths.json"
...rest of tsconfig.json
}

You can't and I am unsure when you will be able to. I have been trying to use baseUrl and paths so I can avoid relative imports but as you can see they are intentionally removing certain values. The "(yet)" is encouraging but (sigh) who knows when they will officially be supporting it. I recommend subscribing to this github issue to be alerted if/when this changes.

The following changes are being made to your tsconfig.json file:
- compilerOptions.baseUrl must not be set (absolute imports are not supported (yet))
- compilerOptions.paths must not be set (aliased imports are not supported)

Create React App does not currently support baseUrl. However there is a workaround...to setup baseUrl for both webpack and the IDE you have to do the following:

  1. Create a .env file with the following code:
NODE_PATH=./
  1. Create a tsconfig.paths.json file with the following code inside:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"src/*": ["*"]
}
}
}
  1. Add the following line to tsconfig.json
{
"extends": "./tsconfig.paths.json",
...
}

I had a similar issue to this general problem (CRA overwrites "noEmit": false in my tsconfig.json of a React library I'm working on where I have two separate builds, one for local development, and another to build the production library with typings). Simple solution: use sed in a postbuild script in the package.json. For example: In-place edits with sed on OS X .

{
...
"scripts": {
...
"postbuild": "sed -i '' 's/{THING CRA IS REPLACING}/{WHAT YOU ACTUALLY WANT}/g' tsconfig.json # CRA is too opinionated on this one.",
...
}
...
}

This approach, however, is not cross-platform (unlike how rimraf is the cross-platform alternative to rm -rf).

If you are using react-scripts 4.0.0 like me then all you need to do is remove the line (around line 160 on my end):

paths: { value: undefined, reason: 'aliased imports are not supported' }

from the file node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js

I was able to straight up add my baseUrl and paths config to my tsconfig.json file like so:

{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@domain/*": ["../src/domain/*"],
},
}
}

and finally compile and move on with my life.

Per usual, YMMV. Please test your stuff. This is obviously a hack but it worked for me so I'm posting here in case it helps someone.

Here's a patch if you feel like sharing with your team:

diff --git a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
index 00139ee..5ccf099 100644
--- a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
+++ b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
@@ -156,7 +156,8 @@ function verifyTypeScriptSetup() {
: 'react',
reason: 'to support the new JSX transform in React 17',
},
-    paths: { value: undefined, reason: 'aliased imports are not supported' },
+    // Removed this line so I can add paths to my tsconfig file
+    // paths: { value: undefined, reason: 'aliased imports are not supported' },
};

Edit

Per @Bartekus thoughtful suggestion in the comments thread I'm adding information on the package I use when I need to add (possibly) temporary changes like these to an npm package: patch-package

The package essentially provides a way to make changes to a package in a cleaner way. Especially when you consider collaboration it becomes very cumbersome to directly change an npm file and move on. The next time you update that package or even when you start developing in a new machine and run npm install your changes will be lost. Also, if you have teammates working on the same project they would never inherit the changes.

In essence you go through the following steps to patch a package:

# fix a bug in one of your dependencies
vim node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js


# run patch-package to create a .patch file
npx patch-package react-scripts


# commit the patch file to share the fix with your team
git add patches/react-scripts+4.0.0.patch
git commit -m "Enable aliased imports in react-scripts"

Next time someone checks out the project and installs it, the patch will be applied automatically due to a post-install script you add during set up:

 "scripts": {
+  "postinstall": "patch-package"
}

See up to date instructions in the package's documentation

For macOS this workaround should work.

package.json

"scripts": {
"start": "osascript -e 'tell app \"Terminal\" to do script \"cd $PATH_TO_REACT_APP && node ./setNoEmitFalse\"' && react-scripts start",
...
},
...

setNoEmitFalse.js

const fs = require('fs');
const { sleep } = require('sleep')
const path = './tsconfig.json'
const run = async () => {
sleep(2)
const tsconfig = fs.readFileSync(path, 'utf-8');
const fixed = tsconfig.replace('"noEmit": true', '"noEmit": false');
fs.writeFileSync(path, fixed)
}
run()

The execution of the javascript file in a separate terminal (osascript) provides the normal output for react-scripts in the original terminal.

For me, the problem was with VSCode using an older version of typescript (4.0.3), while the typescript version shipped with the project is (4.1.2).

The following did the trick for me:

  1. Go to the command palette CTRL+Shift+P.
  2. Choose "TypeScript: Select a TypeScript Version...".
  3. Choose "Use workspace Version".

Go to node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js and replace

const compilerOptions = {
...
};

by

const compilerOptions = { };

On Botpress (with react-scripts 4.0.3), we use a combination of 2 tricks to use paths without ejecting or patching the code. As Glenn and Microcipcip said, the first step is to extend the tsconfig.json file

tsconfig.path.json

{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"~/*": ["src/*"],
"common/*": ["../bp/src/common/*"]
}
}
}

tsconfig.json

{
...
"extends": "./tsconfig.paths.json"
}

Then to make it work in the background, use the package react-app-rewired. It allows to make slight adjustments to the webpack configuration without actually ejecting CRA.

config-overrides.js

module.exports = {
webpack: (config, env) => {
config.resolve.alias['common'] = path.join(__dirname, '../bp/dist/common')
config.resolve.alias['~'] = path.join(__dirname, './src')
}
}

To see the full code, you can check the github repository https://github.com/botpress/botpress/tree/master/packages/ui-admin