Description
Demo Repo
https://github.com/yf-yang/ts-bundler-bug
Which of the following problems are you reporting?
Something else more complicated which I'll explain in more detail
Demonstrate the defect described above with a code sample.
import type { Generic, InterFaceB, InterFaceC } from 'base';
Run tsc --showConfig
and paste its output here
{
"compilerOptions": {
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": false,
"noImplicitReturns": true,
"sourceMap": true,
"noUncheckedIndexedAccess": true,
"strict": true,
"target": "esnext",
"esModuleInterop": true,
"skipLibCheck": true,
"declarationMap": true,
"extendedDiagnostics": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"composite": true,
"rootDir": "./src",
"baseUrl": "./src",
"outDir": "./dist",
"paths": {
"@": [
"."
],
"@/*": [
"*"
]
}
},
"references": [
{
"path": "../../packages/base"
}
],
"files": [
"./src/index.ts"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"../../node_modules"
]
}
Run tsc --traceResolution
and paste its output here
Too long, I'll skip this part since I've figured out how tsc executes and mention that in the comments below.
Paste the package.json
of the importing module, if it exists
{
"name": "derived",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": "./src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc"
},
"dependencies": {
"base": "workspace:^1.0.0"
}
}
Paste the package.json
of the target module, if it exists
{
"name": "base",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": "./src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc"
}
}
Any other comments can go here
What I am expecting
I am developing a monorepo (specifically, that is for web, with webpack as the bundler). In the example, there are two projects in the monorepo, base
and derived
, and base
is a dependency of derived
.
A good feature of the bundler is live update. Whenever I change anything in the base
, the change is captured by the dev server provided by the bundler, so I don't need to build base
again to view the change in the browser.
Therefore, I am expecting the same stuff with moduleResolution: bundler
option. To be specifically, when I change a type annotation of something in base
, I can instantly find the type imported in derived
is also changed without building base
. That is also implemented by tsserver.
However, after reading #51669, it seems apply change to packages that importing this one
is not what moduleResolution: bundler
is originally designed for. Therefore, Without building base
first, I am unable to run tsc
in derived
repo.
That is OK for development, since the build step can only be executed once after everything is done with the language server. However, the typescript-eslint plugin is also using the typechecker, and the way it using lib/typescript.js is pretty similar to tsc
, which means without compiling the base
first, the typescript-eslint plugin will be broken. Every imported types fall back to any
and trigger many rules that prevents any
. That's pretty painful for development.
Why this is happening
After some debugging, I figure out the root cause is here:
TypeScript/src/compiler/program.ts
Lines 3661 to 3678 in 16a36a7
Since base
is a project reference folder from derived
's tsconfig's perspective, its files are always redirected. After reading the code, I find the file name is either directly be converted to .d.ts
if outDir
is not specified or be converted to a .d.ts
file in the outDir
. Therefore, it is not viable to make typescript-eslint work merely via directly setting exports
to src/index.ts
.
The question
After reading the code and the original PR, I want to confirm one question:
Is it possible to build derived
(actually, is it possible to make typescript-eslint get the correct type signature from derived
) without having base
built? How can I achieve the goal, or is it something that the typescript maintenance team cares about?