Skip to content

Commit 7c58011

Browse files
authored
Set impliedNodeFormat on sourceFile we get from host to ensure the field is set correctly (#50977)
* Add test that fails * Handle impliedNodeFormat when handling sourceFileCache Fixes #50872 * Revert the fix * Make sure impliedNodeFormat is set for the sourceFile * Revert "Make sure impliedNodeFormat is set for the sourceFile" This reverts commit 651a47f. * Revert "Revert the fix" This reverts commit 5c98b9c. * Swap the keys for map
1 parent cfa55f1 commit 7c58011

File tree

5 files changed

+203
-14
lines changed

5 files changed

+203
-14
lines changed

src/compiler/program.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ namespace ts {
168168
const originalDirectoryExists = host.directoryExists;
169169
const originalCreateDirectory = host.createDirectory;
170170
const originalWriteFile = host.writeFile;
171-
const readFileCache = new Map<string, string | false>();
172-
const fileExistsCache = new Map<string, boolean>();
173-
const directoryExistsCache = new Map<string, boolean>();
174-
const sourceFileCache = new Map<string, SourceFile>();
171+
const readFileCache = new Map<Path, string | false>();
172+
const fileExistsCache = new Map<Path, boolean>();
173+
const directoryExistsCache = new Map<Path, boolean>();
174+
const sourceFileCache = new Map<SourceFile["impliedNodeFormat"], ESMap<Path, SourceFile>>();
175175

176176
const readFileWithCache = (fileName: string): string | undefined => {
177177
const key = toPath(fileName);
@@ -196,14 +196,16 @@ namespace ts {
196196
return setReadFileCache(key, fileName);
197197
};
198198

199-
const getSourceFileWithCache: CompilerHost["getSourceFile"] | undefined = getSourceFile ? (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
199+
const getSourceFileWithCache: CompilerHost["getSourceFile"] | undefined = getSourceFile ? (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
200200
const key = toPath(fileName);
201-
const value = sourceFileCache.get(key);
201+
const impliedNodeFormat: SourceFile["impliedNodeFormat"] = typeof languageVersionOrOptions === "object" ? languageVersionOrOptions.impliedNodeFormat : undefined;
202+
const forImpliedNodeFormat = sourceFileCache.get(impliedNodeFormat);
203+
const value = forImpliedNodeFormat?.get(key);
202204
if (value) return value;
203205

204-
const sourceFile = getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
206+
const sourceFile = getSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
205207
if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
206-
sourceFileCache.set(key, sourceFile);
208+
sourceFileCache.set(impliedNodeFormat, (forImpliedNodeFormat || new Map()).set(key, sourceFile));
207209
}
208210
return sourceFile;
209211
} : undefined;
@@ -225,13 +227,15 @@ namespace ts {
225227
const value = readFileCache.get(key);
226228
if (value !== undefined && value !== data) {
227229
readFileCache.delete(key);
228-
sourceFileCache.delete(key);
230+
sourceFileCache.forEach(map => map.delete(key));
229231
}
230232
else if (getSourceFileWithCache) {
231-
const sourceFile = sourceFileCache.get(key);
232-
if (sourceFile && sourceFile.text !== data) {
233-
sourceFileCache.delete(key);
234-
}
233+
sourceFileCache.forEach(map => {
234+
const sourceFile = map.get(key);
235+
if (sourceFile && sourceFile.text !== data) {
236+
map.delete(key);
237+
}
238+
});
235239
}
236240
originalWriteFile.call(host, fileName, data, ...rest);
237241
};

src/harness/fakesHosts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace fakes {
4141
}
4242

4343
public write(message: string) {
44+
if (ts.Debug.isDebugging) console.log(message);
4445
this.output.push(message);
4546
}
4647

src/harness/virtualFileSystemWithWatch.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,7 @@ interface Array<T> { length: number; [n: number]: T; }`
985985
}
986986

987987
write(message: string) {
988+
if (Debug.isDebugging) console.log(message);
988989
this.output.push(message);
989990
}
990991

src/testRunner/unittests/tsbuild/moduleResolution.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,36 @@ namespace ts.tscWatch {
8686
commandLineArgs: ["-b", "/src/packages/pkg1.tsconfig.json", "/src/packages/pkg2.tsconfig.json", "--verbose", "--traceResolution"],
8787
});
8888
});
89-
}
89+
90+
describe("unittests:: tsbuild:: moduleResolution:: impliedNodeFormat differs between projects for shared file", () => {
91+
verifyTscWithEdits({
92+
scenario: "moduleResolution",
93+
subScenario: "impliedNodeFormat differs between projects for shared file",
94+
fs: () => loadProjectFromFiles({
95+
"/src/projects/a/src/index.ts": "",
96+
"/src/projects/a/tsconfig.json": JSON.stringify({
97+
compilerOptions: { strict: true }
98+
}),
99+
"/src/projects/b/src/index.ts": Utils.dedent`
100+
import pg from "pg";
101+
pg.foo();
102+
`,
103+
"/src/projects/b/tsconfig.json": JSON.stringify({
104+
compilerOptions: { strict: true, module: "node16" }
105+
}),
106+
"/src/projects/b/package.json": JSON.stringify({
107+
name: "b",
108+
type: "module"
109+
}),
110+
"/src/projects/node_modules/@types/pg/index.d.ts": "export function foo(): void;",
111+
"/src/projects/node_modules/@types/pg/package.json": JSON.stringify({
112+
name: "@types/pg",
113+
types: "index.d.ts",
114+
}),
115+
}),
116+
modifyFs: fs => fs.writeFileSync("/lib/lib.es2022.full.d.ts", libFile.content),
117+
commandLineArgs: ["-b", "/src/projects/a", "/src/projects/b", "--verbose", "--traceResolution", "--explainFiles"],
118+
edits: noChangeOnlyRuns
119+
});
120+
});
121+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
Input::
2+
//// [/lib/lib.d.ts]
3+
/// <reference no-default-lib="true"/>
4+
interface Boolean {}
5+
interface Function {}
6+
interface CallableFunction {}
7+
interface NewableFunction {}
8+
interface IArguments {}
9+
interface Number { toExponential: any; }
10+
interface Object {}
11+
interface RegExp {}
12+
interface String { charAt: any; }
13+
interface Array<T> { length: number; [n: number]: T; }
14+
interface ReadonlyArray<T> {}
15+
declare const console: { log(msg: any): void; };
16+
17+
//// [/lib/lib.es2022.full.d.ts]
18+
/// <reference no-default-lib="true"/>
19+
interface Boolean {}
20+
interface Function {}
21+
interface CallableFunction {}
22+
interface NewableFunction {}
23+
interface IArguments {}
24+
interface Number { toExponential: any; }
25+
interface Object {}
26+
interface RegExp {}
27+
interface String { charAt: any; }
28+
interface Array<T> { length: number; [n: number]: T; }
29+
30+
//// [/src/projects/a/src/index.ts]
31+
32+
33+
//// [/src/projects/a/tsconfig.json]
34+
{"compilerOptions":{"strict":true}}
35+
36+
//// [/src/projects/b/package.json]
37+
{"name":"b","type":"module"}
38+
39+
//// [/src/projects/b/src/index.ts]
40+
import pg from "pg";
41+
pg.foo();
42+
43+
44+
//// [/src/projects/b/tsconfig.json]
45+
{"compilerOptions":{"strict":true,"module":"node16"}}
46+
47+
//// [/src/projects/node_modules/@types/pg/index.d.ts]
48+
export function foo(): void;
49+
50+
//// [/src/projects/node_modules/@types/pg/package.json]
51+
{"name":"@types/pg","types":"index.d.ts"}
52+
53+
54+
55+
Output::
56+
/lib/tsc -b /src/projects/a /src/projects/b --verbose --traceResolution --explainFiles
57+
[12:00:22 AM] Projects in this build:
58+
* src/projects/a/tsconfig.json
59+
* src/projects/b/tsconfig.json
60+
61+
[12:00:23 AM] Project 'src/projects/a/tsconfig.json' is out of date because output file 'src/projects/a/src/index.js' does not exist
62+
63+
[12:00:24 AM] Building project '/src/projects/a/tsconfig.json'...
64+
65+
======== Resolving type reference directive 'pg', containing file '/src/projects/a/__inferred type names__.ts', root directory '/src/projects/node_modules/@types'. ========
66+
Resolving with primary search path '/src/projects/node_modules/@types'.
67+
Found 'package.json' at '/src/projects/node_modules/@types/pg/package.json'.
68+
'package.json' does not have a 'typesVersions' field.
69+
'package.json' does not have a 'typings' field.
70+
'package.json' has 'types' field 'index.d.ts' that references '/src/projects/node_modules/@types/pg/index.d.ts'.
71+
File '/src/projects/node_modules/@types/pg/index.d.ts' exist - use it as a name resolution result.
72+
Resolving real path for '/src/projects/node_modules/@types/pg/index.d.ts', result '/src/projects/node_modules/@types/pg/index.d.ts'.
73+
======== Type reference directive 'pg' was successfully resolved to '/src/projects/node_modules/@types/pg/index.d.ts', primary: true. ========
74+
lib/lib.d.ts
75+
Default library for target 'es3'
76+
src/projects/a/src/index.ts
77+
Matched by default include pattern '**/*'
78+
src/projects/node_modules/@types/pg/index.d.ts
79+
Entry point for implicit type library 'pg'
80+
[12:00:26 AM] Project 'src/projects/b/tsconfig.json' is out of date because output file 'src/projects/b/src/index.js' does not exist
81+
82+
[12:00:27 AM] Building project '/src/projects/b/tsconfig.json'...
83+
84+
File '/src/projects/b/src/package.json' does not exist.
85+
Found 'package.json' at '/src/projects/b/package.json'.
86+
'package.json' does not have a 'typesVersions' field.
87+
======== Resolving module 'pg' from '/src/projects/b/src/index.ts'. ========
88+
Module resolution kind is not specified, using 'Node16'.
89+
Resolving in ESM mode with conditions 'node', 'import', 'types'.
90+
File '/src/projects/b/src/package.json' does not exist according to earlier cached lookups.
91+
File '/src/projects/b/package.json' exists according to earlier cached lookups.
92+
Loading module 'pg' from 'node_modules' folder, target file type 'TypeScript'.
93+
Directory '/src/projects/b/src/node_modules' does not exist, skipping all lookups in it.
94+
Directory '/src/projects/b/node_modules' does not exist, skipping all lookups in it.
95+
File '/src/projects/node_modules/@types/pg/package.json' exists according to earlier cached lookups.
96+
'package.json' does not have a 'typings' field.
97+
'package.json' has 'types' field 'index.d.ts' that references '/src/projects/node_modules/@types/pg/index.d.ts'.
98+
File '/src/projects/node_modules/@types/pg/index.d.ts' exist - use it as a name resolution result.
99+
Resolving real path for '/src/projects/node_modules/@types/pg/index.d.ts', result '/src/projects/node_modules/@types/pg/index.d.ts'.
100+
======== Module name 'pg' was successfully resolved to '/src/projects/node_modules/@types/pg/index.d.ts'. ========
101+
File '/src/projects/node_modules/@types/pg/package.json' exists according to earlier cached lookups.
102+
======== Resolving type reference directive 'pg', containing file '/src/projects/b/__inferred type names__.ts', root directory '/src/projects/node_modules/@types'. ========
103+
Resolving with primary search path '/src/projects/node_modules/@types'.
104+
File '/src/projects/node_modules/@types/pg/package.json' exists according to earlier cached lookups.
105+
'package.json' does not have a 'typings' field.
106+
'package.json' has 'types' field 'index.d.ts' that references '/src/projects/node_modules/@types/pg/index.d.ts'.
107+
File '/src/projects/node_modules/@types/pg/index.d.ts' exist - use it as a name resolution result.
108+
Resolving real path for '/src/projects/node_modules/@types/pg/index.d.ts', result '/src/projects/node_modules/@types/pg/index.d.ts'.
109+
======== Type reference directive 'pg' was successfully resolved to '/src/projects/node_modules/@types/pg/index.d.ts', primary: true. ========
110+
File '/lib/package.json' does not exist.
111+
File '/package.json' does not exist.
112+
lib/lib.es2022.full.d.ts
113+
Default library for target 'es2022'
114+
src/projects/node_modules/@types/pg/index.d.ts
115+
Imported via "pg" from file 'src/projects/b/src/index.ts'
116+
Entry point for implicit type library 'pg'
117+
File is CommonJS module because 'src/projects/node_modules/@types/pg/package.json' does not have field "type"
118+
src/projects/b/src/index.ts
119+
Matched by default include pattern '**/*'
120+
File is ECMAScript module because 'src/projects/b/package.json' has field "type" with value "module"
121+
exitCode:: ExitStatus.Success
122+
123+
124+
//// [/src/projects/a/src/index.js]
125+
"use strict";
126+
127+
128+
//// [/src/projects/b/src/index.js]
129+
import pg from "pg";
130+
pg.foo();
131+
132+
133+
134+
135+
Change:: no-change-run
136+
Input::
137+
138+
139+
Output::
140+
/lib/tsc -b /src/projects/a /src/projects/b --verbose --traceResolution --explainFiles
141+
[12:00:29 AM] Projects in this build:
142+
* src/projects/a/tsconfig.json
143+
* src/projects/b/tsconfig.json
144+
145+
[12:00:30 AM] Project 'src/projects/a/tsconfig.json' is up to date because newest input 'src/projects/a/src/index.ts' is older than output 'src/projects/a/src/index.js'
146+
147+
[12:00:31 AM] Project 'src/projects/b/tsconfig.json' is up to date because newest input 'src/projects/b/src/index.ts' is older than output 'src/projects/b/src/index.js'
148+
149+
exitCode:: ExitStatus.Success
150+
151+

0 commit comments

Comments
 (0)