Create-react-app: TypeScript {paths} are not taken into consideration

Created on 26 Oct 2018  Â·  28Comments  Â·  Source: facebook/create-react-app

Edit from maintainers: we want to support this — see https://github.com/facebook/create-react-app/issues/5585#issuecomment-451161631 for details!


Is this a bug report?

Yes.

Did you try recovering your dependencies?

NPM 5.6.0
Yarn 1.7.0

Which terms did you search for in User Guide?

paths, paths map, path mappings, typescript path, typescript paths

Environment

  System:
    OS: macOS High Sierra 10.13.6
    CPU: x64 Intel(R) Core(TM) i7-7567U CPU @ 3.50GHz
  Binaries:
    Node: 8.11.2 - ~/.nvm/versions/node/v8.11.2/bin/node
    Yarn: 1.7.0 - ~/.yarn/bin/yarn
    npm: 5.6.0 - ~/.nvm/versions/node/v8.11.2/bin/npm
  Browsers:
    Chrome: 69.0.3497.100
    Firefox: 61.0.1
    Safari: 12.0
  npmPackages:
    @types/react: ^16.4.18 => 16.4.18 
    react: ^16.6.0 => 16.6.0 
    react-dom: ^16.6.0 => 16.6.0 
    react-scripts: 2.0.6-next.c662dfb0

Steps to Reproduce

TypeScript {paths} in {compilerOptions} are not working.

  1. Clone cra-workspaces#cra-2
  2. yarn install at the root
  3. cd apps/cra
  4. yarn start

Expected Behavior

CRA should resolve the modules listed in {paths}.

Actual Behavior

CRA cannot resolve the modules listed in {paths}.

Reproducible Demo

cra-workspaces#cra-2, instructions listed above.

typescript

Most helpful comment

@Timer Would you care to elaborate on why this decision has been made? Your comments have been rather short and non-informative. Is it really such a technical difficulty or it was left out just annoy developers and never use Typescript with CRA? Once you start using this TypeScript feature you really don't want to go back to these long '../../../..' paths.

All 28 comments

AFAIK @babel/plugin-typescript only used for type-checking and validation, whilst actual emit is made by Babel (so no tsc is involved).

Adding tsconfig-paths-webpack-plugin should solve this issue.

@miraage but this would have to be done by CRA, because I'd like to avoid ejecting the config.

@rolandjitsu Agree. It should have been done by CRA, if I had been right in my assumptions regarding babel/tsc.

@miraage actually, @babel/plugin-typescript loads @babel/plugin-transform-typescript which does the opposite of what you describe, it does not type check, it just transforms the ts to js.

So it's possible that path support needs to be added in both webpack and babel. Furthermore, jest also needs support for this (in react-scripts-ts this is done via jest moduleNameMapper option).

@rolandjitsu

https://babeljs.io/blog/2018/08/27/7.0.0#typescript-support-babel-preset-typescript

We worked with the TypeScript team on getting Babel to parse/transform type syntax with @babel/preset-typescript, similar to how we handle Flow with @babel/preset-flow.

Currently this is done on purpose. We have no immediate plans to respect alternate module resolve modes/paths.

EDIT: This workaround no longer works with the latest CRA versions where the TS config is routinely reset.

This can be achieved without ejecting if you're willing to use react-app-rewired in combination with customize-cra.

There's a big warning on the react-app-rewired readme about it not supporting CRA 2.x, but this is referring to their helpers like injectBabelPlugin and such. The base machinery for allowing you to hook into and override Webpack/Babel configs is still useful. The actual Webpack/Babel config manipulations provided by customize-cra are made for CRA 2.x.

An example, setting up two aliases:

  • @ui points to <root>/src/ui
  • @src points to <root>/src
  1. Update tsconfig.json with your custom paths:
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      "@ui/*": ["ui/*"],
      "@src/*": ["./*"]
    },
    ...
  }
  1. $ yarn add react-app-rewired customize-cra
  2. Change all package.json scripts commands to use react-app-rewired instead of react-scripts.
  3. Create a config-overrides.js file at the project root
  4. Set up your paths overrides using the customize-cra helper addWebpackAlias
const path = require('path')
const { addWebpackAlias } = require('customize-cra')

module.exports = function override(config, env) {
  config = addWebpackAlias({
    ['@ui']: path.resolve(__dirname, 'src', 'ui'),
    ['@src']: path.resolve(__dirname, 'src')
  })(config)

  return config
}
  1. At this point, your path aliases should be working.
  2. To get the aliases working in Jest, add this to your package.json:

```
"jest": {
"moduleNameMapper": {
"^@ui(.)$": "/src/ui$1",
"^@src(.
)$": "/src$1"
}
},
````

The react-app-rewired + customize-cra combo should allow you to change nearly anything you want in CRA without ejecting. I use it for path aliasing and Babel plugins where a macro is unavailable.

Yeah, I currently have the following config which works fine on [email protected] however not here.

 "paths": {
      "~/*": [
        "src/*"
      ]
    },

@Timer while I understand the decision, I do hope that this is somewhere on the roadmap. Clearly, some of us (e.g. @lee-reinhardt) go to great lengths to figure out how to make this work.

I personally believe that it would be a valuable feature to have built in support for paths in CRA 2, and the effort to achieve this should be small, assuming it's just a matter of adding the webpack tsconfig-paths-webpack-plugin plugin.

@jamsch yes, react-scripts-ts does have support for paths. And they're also working on https://github.com/wmonk/create-react-app-typescript/pull/409 to upgrade to CRA 2. But they're debating whether it's worth continuing given that CRA 2 also has support for TS.

The only difference, at the moment, between react-scripts and react-scripts-ts in terms of TS is path support and tslint (more or less). So adding path support here would eliminate the need of using react-scripts-ts which means that you do not need to wait for the maintainers of react-scripts-ts to upgrade CRA (they're usually doing a great job at being in sync with CRA) to get the newest features and that there are less chances for bugs to occur when the sync occurs.

Nonetheless, I'd be more than happy to give this a shot if pointed in the right direction.

EDIT: If babel is in charge of transforming ts to js, perhaps module-resolver could be an option?

I agree with @rolandjitsu, this is a pretty big concern. Every Typescript app I have made exploited Paths as much as possible. One of the beauties of using TS. I currently have a few projects in react-scripts-ts that I was converting over this weekend until I kept running into issues with paths. That's what led me here.

I really hope this is built into CRA sooner rather than later.

FWIW, I've managed to add paths support via craco like this:

First, a craco plugin is needed:

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

module.exports = {
    overrideWebpackConfig: ({ webpackConfig, cracoConfig, pluginOptions, context: { env, paths } }) => {
        webpackConfig.resolve.plugins.push(new TsconfigPathsPlugin({}))
        return webpackConfig;
    }
};

Then, it needs to be added to craco configuration:

const logWebpackConfigPlugin = require("./craco-plugin");

module.exports = {
    plugins: [
        { plugin: logWebpackConfigPlugin }
    ]
}

Hope this will help someone on this thread. However, now I have to wonder whether it's all broken now due to #5609.

@Timer please consider changing value to suggested in #5609 to allow temporary hacks and workaround like the one I cited to continue to work. Thank you for understanding.

Honestly I see paths as quite a necessity for any medium-large project because what inevitably happens is importing from the project root for components that are multiple levels deep which ends up like:

// src/components/ui/Button/Square.tsx
import colors from '../../../../res/colors.ts'; // instead of '~/res/colors.ts'

// src/pages/auth/signin/SignIn.tsx
import * as Button from '../../../../components/ui/Button';  // instead of '~/components/ui/Button'

Any further refactoring (such as moving components around) often end up messing the import path nesting unless IDE refactoring features save you.

This is a must-have for my use case so I'll probably end up ejecting if this isn't on the cards for the CRA team.

Closing as a duplicate of #5118.

TypeScript support will ship without support for baseUrl and paths.
Sorry for any inconvenience! I know it'll be a bit painful to switch back to relative paths.

Edit: we do want to support this in the future, it just didn't make it into the initial release. See this comment: https://github.com/facebook/create-react-app/issues/5585#issuecomment-451161631.

@Timer I'm a bit disappointed to see such resistance to add TS paths support. Paths were quite a big feature in TS and a lot of ppl were very happy to have it.

@Timer Would you care to elaborate on why this decision has been made? Your comments have been rather short and non-informative. Is it really such a technical difficulty or it was left out just annoy developers and never use Typescript with CRA? Once you start using this TypeScript feature you really don't want to go back to these long '../../../..' paths.

We've held this position for a long time. Allowing users to adjust the module resolution order is rather confusing and can lead to subtle bugs (i.e. aliasing something that conflicts with a npm package name).

It's hard to get all tooling to agree on how the resolution is performed, as we need to consider: webpack, Jest, Flow, TypeScript itself, and other tooling end-users install.

This isn't a hard no, but it's likely we'll only add support for absolute imports and not arbitrary aliasing (i.e. baseUrl but not paths).

Due to this complexity, we'll ship support without paths & baseUrl and evaluate it at a later date.

Edit: we do want to support this in the future, it just didn't make it into the initial release. See this comment: https://github.com/facebook/create-react-app/issues/5585#issuecomment-451161631.

This can be achieved without ejecting if you're willing to use react-app-rewired in combination with customize-cra.

There's a big warning on the react-app-rewired readme about it not supporting CRA 2.x, but this is referring to their helpers like injectBabelPlugin and such. The base machinery for allowing you to hook into and override Webpack/Babel configs is still useful. The actual Webpack/Babel config manipulations provided by customize-cra are made for CRA 2.x.

An example, setting up two aliases:

  • @ui points to <root>/src/ui
  • @src points to <root>/src
  1. Update tsconfig.json with your custom paths:
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      "@ui/*": ["ui/*"],
      "@src/*": ["./*"]
    },
    ...
  }
  1. $ yarn add react-app-rewired customize-cra
  2. Change all package.json scripts commands to use react-app-rewired instead of react-scripts.
  3. Create a config-overrides.js file at the project root
  4. Set up your paths overrides using the customize-cra helper addWebpackAlias
const path = require('path')
const { addWebpackAlias } = require('customize-cra')

module.exports = function override(config, env) {
  config = addWebpackAlias({
    ['@ui']: path.resolve(__dirname, 'src', 'ui'),
    ['@src']: path.resolve(__dirname, 'src')
  })(config)

  return config
}
  1. At this point, your path aliases should be working.
  2. To get the aliases working in Jest, add this to your package.json:
  "jest": {
    "moduleNameMapper": {
      "^@ui(.*)$": "<rootDir>/src/ui$1",
      "^@src(.*)$": "<rootDir>/src$1"
    }
  },

The react-app-rewired + customize-cra combo should allow you to change nearly anything you want in CRA without ejecting. I use it for path aliasing and Babel plugins where a macro is unavailable.

[email protected] not work, It will remove baseUrl and paths in tsconfig.json

@izayl

[email protected] not work, It will remove baseUrl and paths in tsconfig.json

You're right. I'm currently stuck on [email protected] with the customize-cra/react-app-rewired config for the time being. Considering forking this repo and at least taking out the part that removes path/baseurl config from the user's tsconfig if there's no plans for implementation.

You can use 2.1.0 (or rather 2.1.1) and use the extends function (https://github.com/facebook/create-react-app/issues/5645#issuecomment-435201019). Works like a charm for my mid-sized Typescript CRA app.

@jamsch Why to fork when there is react-scripts-ts@next? So far it works just fine.

You can use 2.1.0 (or rather 2.1.1) and use the extends function (#5645 (comment)). Works like a charm for my mid-sized Typescript CRA app.

Confirm, it works. But another problem is with "isolatedModules" is being set to "true", and the extending file does not overwrite this parameter.

That's right. I assume that you need this to fix .json imports? I didn't find a solution for this, but since I just have to import a few JSON files, I've converted them to plain JS objects for now.

+1 to add this feature to TS project.

People working with TS need Absolute Path or Alias config feature.

Thanks

You can use 2.1.0 (or rather 2.1.1) and use the extends function (#5645 (comment)). Works like a charm for my mid-sized Typescript CRA app.

This doesn't work for me - the moment I run the server the tsconfig gets reset and apparently the baseUrl doesn't get propagated to webpack. It is a shame that this does not work as expected.

Just to be clear — we do want to support absolute paths.

https://github.com/facebook/create-react-app/issues/5585#issuecomment-433751395 referred to what we ship in the first version with TypeScript to intentionally limit the scope. It doesn't mean we don't want this! :-)

In particular, we want to:

  • Make NODE_PATH work for TS. (https://github.com/facebook/create-react-app/issues/5692)
  • Make @ alias work by default for everyone — both in JS and TS. (https://github.com/facebook/create-react-app/issues/5118)

I'm sorry for the frustration this is causing. We're not asking you to convert your code to use relative paths — but please wait for either of these two issues to resolve.

I'll close this one but you can track either #5692 or #5118, and we'll let you know when we have something!

@gaearon thanks for your response

There is also another plausible alternative — we could embrace tsconfig.json / jsconfig.json, and make paths specified there work across the setup (Webpack/Jest). See https://github.com/facebook/create-react-app/issues/5645 for that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dualcnhq picture dualcnhq  Â·  3Comments

JimmyLv picture JimmyLv  Â·  3Comments

alleroux picture alleroux  Â·  3Comments

Evan-GK picture Evan-GK  Â·  3Comments

Aranir picture Aranir  Â·  3Comments