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
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
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.
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).
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?
Like many oss projects, CRA is maintained mostly by volunteers. If you want this to land soon, you probably need to do the work.
Oh, got that. Thanks again Alex.
API support has been released in 3.6: https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/#apis-to-support-build-and-incremental
ts-loader
project supports the flags since version 6.1.0: https://github.com/TypeStrong/ts-loader/releases/tag/v6.1.0
@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?
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.
Or should we create an issue about typescript 3.6 migration?
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 https://github.com/facebook/create-react-app/issues/7807#issuecomment-541161474, current tooling must support project references. Can someone with knowledge of this tooling create appropriate issues in projects?
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!
Seem like ForkTsCheckerWebpackPlugin
which is used by CRA for typechecking is going to add support of project references pretty soon https://github.com/TypeStrong/fork-ts-checker-webpack-plugin/issues/187#issuecomment-594401306
fork-ts-checker-webpack-plugin v5.0.0 has added support for project references.
@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 ?
@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
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
typescript
to typescript.typescriptPath
tsconfig
to typescript.configFile
reportFiles
attribute to issue.exclude
following info from here https://github.com/TypeStrong/fork-ts-checker-webpack-plugin/issues/450checkSyntacticErrors
to diagnosticOptions.semantic/synctactic
following info from here https://github.com/TypeStrong/ts-loader/pull/1141/commits/71af50443ec6d1c140d5fe5711e0a4404c66dc33silent
to logger.infafrastructure/issues
forkTsCheckerHooks.receive
to forkTsCheckerHooks.issues
typescript.build: true
to activate smart incremental builds, which is necessary to make Project References workResult
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.
cc @piotr-oles - thought this might be interesting to you.
@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)
I've added an example package that emphasise the above issues
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 :-)
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
Most helpful comment
fork-ts-checker-webpack-plugin v5.0.0 has added support for project references.