Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need support for Typescript Project references #6799

Open
ashish-g-lahane opened this issue Apr 11, 2019 · 23 comments
Open

Need support for Typescript Project references #6799

ashish-g-lahane opened this issue Apr 11, 2019 · 23 comments

Comments

@ashish-g-lahane
Copy link

ashish-g-lahane commented Apr 11, 2019

Recently typescript has added the feature of project references:

https://www.typescriptlang.org/docs/handbook/project-references.html

In the projects created with create-react-app (with --typescript switch), we are not able to use this feature of typescript because babel complains that we cannot refer files for compiling which are outside the 'src' folder.

I know that there are other workarounds such as

  • publishing the other projects as modules to the npm repository and then we can install them in the create-react-app
  • we can play around with 'npm link', but this approach is not clean. We have to simulate the project references, instead of using the actual references feature of typescript with this approach.
  • otherwise, we can eject create-react-app, and then customize the options.
  • last option is to do away with create-react-app and build the tool-chain from scratch, which I tried and made it to work with ts-loader instead of babel. In this approach, I had to set the 'projectReferences=true' option for the ts-loader inside webpack.config.js. This worked smoothly. But then I'm loosing on create-react-app, which I don't want.

But all these ways are not clean and involve the trade-off between something or the other.

Having the C# and Java background, I'm pretty much used to the concept of project references. Many will suggest to not go this route, but I do need it. If it was not useful enough, typescript wouldn't have come up with it in the first place.

Also, I'm new to the web development world (just a month old), so pardon me for my lack of knowledge and if I'm asking for something very trivial and already exists.

Thanks
-Ashish

@heyimalex
Copy link
Contributor

I appreciate the thought out issue. I think this is partially a duplicate of #5563 insofar as it's the reason you're getting the current error.

But I'm also not sure it would fix anything. I'm not very familiar with project references, but they look like they introduce new module resolution logic. The way we compile typescript is more complex than just calling tsc, so introducing support will probably not be so trivial. Swapping plugin-transform-typescript for ts-loader would be a major change.

All that to say I'm not sure this will land without someone doing the legwork.

@ashish-g-lahane
Copy link
Author

I appreciate the thought out issue. I think this is partially a duplicate of #5563 insofar as it's the reason you're getting the current error.

But I'm also not sure it would fix anything. I'm not very familiar with project references, but they look like they introduce new module resolution logic. The way we compile typescript is more complex than just calling tsc, so introducing support will probably not be so trivial. Swapping plugin-transform-typescript for ts-loader would be a major change.

All that to say I'm not sure this will land without someone doing the legwork.

Thanks a lot Alex (heyimalex).

@ashish-g-lahane
Copy link
Author

ashish-g-lahane commented Apr 25, 2019

Hi CRA team, can we please prioritise this feature? We need it as soon as possible.

About the work involved, it may boil down to the support from babel for project references and then CRA just incorporating that option inbuilt. For example, (excerpt from my original post from the top)


last option is to do away with create-react-app and build the tool-chain from scratch, which I tried and made it to work with ts-loader instead of babel. In this approach, I had to set the 'projectReferences=true' option for the ts-loader inside webpack.config.js. This worked smoothly. But then I'm loosing on create-react-app, which I don't want.


So if the ts-loader can support it, I wonder why babel is taking so long to support it? Or maybe, babel already supports it and CRA just needs to use that option?

@heyimalex
Copy link
Contributor

Like many oss projects, CRA is maintained mostly by volunteers. If you want this to land soon, you probably need to do the work.

@ashish-g-lahane
Copy link
Author

Oh, got that. Thanks again Alex.

@kirill-konshin
Copy link

@tommedema
Copy link

tommedema commented Sep 12, 2019

@kirill-konshin that seems great but how would we benefit from this in CRA?

Meanwhile is there a workaround to use typescript references in a CRA app?

@kirill-konshin
Copy link

There is a workaround to run tsc --build && (tsc --build --watch & react-scripts start): build, then run watch & start in parallel.

This is fine if referenced project is using incremetal: true then performance will not be as bad. The problem is that app itself will be also built, so probably a separate tsconfig with references is needed.

Direct benefit is that if CRA is part of monorepo project, which also has a library package and React Native, so that library code is shared between Web and Native. In this case users have to manually build & watch library separately somehow instead of just relying on TSC functionality which can build all upstream projects if necessary.

@Toub
Copy link

Toub commented Sep 25, 2019

Or should we create an issue about typescript 3.6 migration?

@Bessonov
Copy link

Bessonov commented Dec 8, 2019

Need this feature as well. Current state of monorepo + typescript isn't very satisfying, because with different approaches we run in different errors. Currently, we must create es5 modules to satisfy react, but even with incremental builds, use --watch is very slow for every change and makes tree-shaking difficult.

As stated in #7807 (comment), current tooling must support project references. Can someone with knowledge of this tooling create appropriate issues in projects?

@DakotaLarson
Copy link

DakotaLarson commented Jan 1, 2020

I just posted a future tumbleweed about this: https://stackoverflow.com/questions/59555827/sharing-code-between-typescript-projects-with-react.

I got a partial solution using customize-cra.

const { override, removeModuleScopePlugin, getBabelLoader } = require("customize-cra");
const path = require("path");

const addCommon = (config) => {
  const loader = getBabelLoader(config, false);
  const commonPath = path.normalize(path.join(process.cwd(), "../common")).replace(/\\/g, "\\");
  loader.include = [loader.include, commonPath];
  return config;
};
module.exports = override(
  addCommon,
  removeModuleScopePlugin(),
);

I don't have intellisense, (hence the question), but this might be useful to someone. It essentially includes the common folder along with src for babel-loader and removes ModuleScopePlugin. Apparently ts-loader supports project references when the option is enabled.
I tried tinkering with trying to get ts-loader integrated, but I found this "solution" first.
A cleaner solution is certainly appreciated!

acheronfail added a commit to acheronfail/rttw that referenced this issue Feb 18, 2020
Note: we currently have to use `tsc --build && react-scripts start`
in the client's package.json as a workaround since create-react-app
doesn't support TypeScript's Project References right now.
See: facebook/create-react-app#6799 (comment)
@ivankoleda
Copy link

Seem like ForkTsCheckerWebpackPlugin which is used by CRA for typechecking is going to add support of project references pretty soon TypeStrong/fork-ts-checker-webpack-plugin#187 (comment)

@SanjoSolutions
Copy link

fork-ts-checker-webpack-plugin v5.0.0 has added support for project references.

@darbaidze2020
Copy link

@heyimalex Alex, what about last comment?!

fork-ts-checker-webpack-plugin v5.0.0 has added support for project references.

All we need is to upgrade fork-ts-checker-webpack-plugin dependency to 5.0.0 version, right?.... is this not enough for supporting project references ?

@MartinDevillers
Copy link

@heyimalex Alex, what about last comment?!

fork-ts-checker-webpack-plugin v5.0.0 has added support for project references.

All we need is to upgrade fork-ts-checker-webpack-plugin dependency to 5.0.0 version, right?.... is this not enough for supporting project references ?

Just tried this locally by overriding fork-ts-checker-webpack-plugin to version 5.0.0 using npm-force-resolutions and now react-script start errors out with ForkTsCheckerWebpackPlugin Invalid Options.

Looking around the fork-ts-checker-webpack-plugin repo I see breaking changes in the configuration between 4.1.6 and 5.0.0

@MartinDevillers
Copy link

MartinDevillers commented Oct 8, 2020

I spent some time today to see if this could be fixed. Full changes including a test library and test project available here:

https://github.com/facebook/create-react-app/compare/master...MartinDevillers:fix-ts-project-references?expand=1

Summary

  1. Change typescript to typescript.typescriptPath
  2. Change tsconfig to typescript.configFile
  3. Change reportFiles attribute to issue.exclude following info from here 5.0.1: The "files" glob patterns is not working properly TypeStrong/fork-ts-checker-webpack-plugin#450
  4. Change checkSyntacticErrors to diagnosticOptions.semantic/synctactic following info from here TypeStrong/ts-loader@71af504
  5. Change silent to logger.infafrastructure/issues
  6. Change forkTsCheckerHooks.receive to forkTsCheckerHooks.issues
  7. Added typescript.build: true to activate smart incremental builds, which is necessary to make Project References work

Result
After these changes I created a fresh react app called my-react-app, and added a reference to tsconfig.json that points to another TypeScript project called my-lib. When I run npm start inside my-react-app a tsconfig.tsbuildinfo is created in the referenced projects directory my-lib, so TypeScript's new Build Mode is picking up the project reference and doing something with it. Yay.

Challenges
However, I am running into difficulties when I try to use a method from the library inside the my-react-app project. I first tried to add a relative reference to App.tsx like this: import { greeting } from '../../my-lib/src/helloWorld'. But I got slapped by the "relative imports outside of src/ are not supported" check from the ModuleScopePlugin. After disabling this plugin the error changed to:

Module parse failed: Unexpected token (8:29)
File was processed with these loaders:
 * ./node_modules/@pmmmwh/react-refresh-webpack-plugin/loader/index.js
You may need an additional loader to handle the result of these loaders.

So I suppose the build output from the referenced project is not being loaded (correctly). Running a manual tsc on the referenced my-lib project creates a /dist folder as expected. Referencing the build output directly with import { greeting } from '../../my-lib/dist/helloWorld' works, but I don't feel this is the way to go.

Preferably, I would like to use a path alias to include a project reference. First, I add both the path alias and the reference to my my-react-app/tsconfig.json:

  "paths": {
    "@my-lib/*": ["../my-lib/src/*"]
  },
  "references": [
    {
      "path": "../my-lib"
    }
  ]

Second, I reference stuff from said library by using import { greeting } from '@my-lib/helloWorld'. This looks clean and passes the ModuleScopePlugin check.

My savviness of create-react-app and it's underlying dependencies like babel-loader and fork-ts-checker-webpack-plugin is limited, so I'm hoping this info helps us move forward.

@johnnyreilly
Copy link
Contributor

cc @piotr-oles - thought this might be interesting to you.

@Bnaya
Copy link

Bnaya commented Oct 10, 2020

@MartinDevillers thank you very much for the effort!

I've created a spinoff of your code, that makes project refs work when the sub projects sits under the src folder of the CRA app
https://github.com/MartinDevillers/create-react-app/pull/1/files

The sub project tsconfig does kicks in,
For demonstration I've set there a more relaxed noImpliclitAny: false while in the parent project it's still strict.

The next challenges would be to: (And that's not related to typescript, but CTA limitation in general)

  • Consume package that does not sit under src/or the same CRA app root dir at all (Like in mono repo)
  • Work with typescript project references automatic package.json dist/src mapping (when package.json main points to .js file, tsc knows to locate locate the appropriate .ts file)

I've added an example package that emphasise the above issues

@MartinDevillers
Copy link

MartinDevillers commented Oct 11, 2020

Thanks for the input @Bnaya ! This looks promising. While it doesn't fit my use-case, which is code-reusability across different projects, this would definitely be beneficial to other people who are working on large CRA-based applications. Having the ability to split a CRA-codebase into sub-projects improves transpile performance, which helps to maintain overall Developer Experience.

Just to echo OP's motivations for this feature request I'd like to add my own 2 cents below. Read if you're interested:

Like OP, I have a strong background in large-scale Java and C# application development (and a not-so-strong background in React+TypeScript). A typical project structure from the Java/C# world has many different sub-projects at the top-level. These sub-projects can be roughly classified as either 1) applications (e.g. a REST-API; a web-interface; a mobile app) and 2) libraries (e.g. contracts; utilities; shared models; DTOs; etc). Applications may depend on libraries but not on each other. Libraries may depend on other libraries.

Besides reusability of code, the other reason why we do this in our ecosystems is to maintain DevEx by avoiding high build times. Since both Java and C# are compiled languages, any change to the code has to be followed by a compile (and possible application restart) in order to be reflected in a live environment. However, the tooling is smart in the sense that any sub-projects that haven't been changed, will not be rebuild and their previous build output (.dll, .jar, .class, etc) will be reused.

Obviously, JavaScript (or should I call it ECMAScript now?) being an interpreted language, it doesn't suffer from the build performance problem, which is an enormous benefit of this tech-stack. Leaving linters and packers out of the picture; it's just text files pointing at other text files. All is well. TypeScript is interesting since it's a compiled language that outputs an interpreted language. So given a non-trivial codebase, build times will add up. I believe Project References was introduced by the TS-team to alleviate this issue. It's pretty much identical to the behavior from Project References in a C# solution (even the term is the same). It allows a developer to split a large codebase into smaller units that each produce their own consumable build artifact.

So anyway, I've been researching ways to incorporate this behavior in my own CRA project. All the solutions I've found so far feel hacky or have other serious drawbacks. TypeScript Project References, assuming it works, looks like the most mature solution to this problem so far, which is why I'm here asking for support :-)

@Bnaya
Copy link

Bnaya commented Oct 11, 2020

Javascript toolchains are composed of independent tools that not moving in the same pace or direction.

In that case, CRA have explict behaviour of not transpiling files outside of the src dir (which makes monorepo tricky out of the box)
And also ts project refs have that smart source resolving that is not standart by no mean.
Until such things will get speces/stabelized other tools might avoid supporting it out of the box

You can use tools like craco to change the cra without ejecting and make it work for your use case

@ThijmenDam
Copy link

ThijmenDam commented Jan 12, 2022

@MartinDevillers Adding to the post you wrote at October 8 2020 - we got stuck at the exact issue you described when trying to have shared TS project between our frontend and backend. Importing the source files resulted in the You may need an additional loader to handle the result of these loaders. as well (after circumventing the ModuleScopePlugin). How wo managed to "solve" this issue is by creating an alias to the build files (rather than the source files) in our tsconfig.json.

@yakovliam
Copy link

Any official update on this issue? Also ran into ThijmenDam's issue with importing the source files.

@cmcnicholas
Copy link

Also interested in this, we have a large project that I would like to "drop in" a react native app alongside other projects and leverage all the code we have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests