Skip to content

Commit 2bfa980

Browse files
committed
If we are updating dts of any of the file and it affects global scope, everything needs update in signature and dts emit
Fixes #42769
1 parent 9811d70 commit 2bfa980

3 files changed

+105
-21
lines changed

src/compiler/builder.ts

+47-11
Original file line numberDiff line numberDiff line change
@@ -542,18 +542,48 @@ namespace ts {
542542
return newSignature !== oldSignature;
543543
}
544544

545-
function forEachKeyOfExportedModulesMap(state: BuilderProgramState, filePath: Path, fn: (exportedFromPath: Path) => void) {
545+
function forEachKeyOfExportedModulesMap<T>(
546+
state: BuilderProgramState,
547+
filePath: Path,
548+
fn: (exportedFromPath: Path) => T | undefined,
549+
): T | undefined {
546550
// Go through exported modules from cache first
547-
state.currentAffectedFilesExportedModulesMap!.getKeys(filePath)?.forEach(fn);
551+
let keys = state.currentAffectedFilesExportedModulesMap!.getKeys(filePath);
552+
const result = keys && forEachKey(keys, fn);
553+
if (result) return result;
554+
548555
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
549-
state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
556+
keys = state.exportedModulesMap!.getKeys(filePath);
557+
return keys && forEachKey(keys, exportedFromPath =>
550558
// If the cache had an updated value, skip
551559
!state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
552-
!state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) &&
553-
fn(exportedFromPath)
560+
!state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) ?
561+
fn(exportedFromPath) :
562+
undefined
554563
);
555564
}
556565

566+
function handleDtsMayChangeOfGlobalScope(
567+
state: BuilderProgramState,
568+
filePath: Path,
569+
cancellationToken: CancellationToken | undefined,
570+
computeHash: BuilderState.ComputeHash,
571+
host: BuilderProgramHost,
572+
): boolean {
573+
if (!state.fileInfos.get(filePath)?.affectsGlobalScope) return false;
574+
// Every file needs to be handled
575+
BuilderState.getAllFilesExcludingDefaultLibraryFile(state, state.program!, /*firstSourceFile*/ undefined)
576+
.forEach(file => handleDtsMayChangeOf(
577+
state,
578+
file.resolvedPath,
579+
cancellationToken,
580+
computeHash,
581+
host,
582+
));
583+
removeDiagnosticsOfLibraryFiles(state);
584+
return true;
585+
}
586+
557587
/**
558588
* Iterate on referencing modules that export entities from affected file and delete diagnostics and add pending emit
559589
*/
@@ -579,6 +609,7 @@ namespace ts {
579609
const currentPath = queue.pop()!;
580610
if (!seenFileNamesMap.has(currentPath)) {
581611
seenFileNamesMap.set(currentPath, true);
612+
if (handleDtsMayChangeOfGlobalScope(state, currentPath, cancellationToken, computeHash, host)) return;
582613
handleDtsMayChangeOf(state, currentPath, cancellationToken, computeHash, host);
583614
if (isChangedSignature(state, currentPath)) {
584615
const currentSourceFile = Debug.checkDefined(state.program).getSourceFileByPath(currentPath)!;
@@ -592,8 +623,10 @@ namespace ts {
592623
const seenFileAndExportsOfFile = new Set<string>();
593624
// Go through exported modules from cache first
594625
// If exported modules has path, all files referencing file exported from are affected
595-
forEachKeyOfExportedModulesMap(state, affectedFile.resolvedPath, exportedFromPath =>
596-
state.referencedMap!.getKeys(exportedFromPath)?.forEach(filePath =>
626+
forEachKeyOfExportedModulesMap(state, affectedFile.resolvedPath, exportedFromPath => {
627+
if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, computeHash, host)) return true;
628+
const references = state.referencedMap!.getKeys(exportedFromPath);
629+
return references && forEachKey(references, filePath =>
597630
handleDtsMayChangeOfFileAndExportsOfFile(
598631
state,
599632
filePath,
@@ -602,12 +635,13 @@ namespace ts {
602635
computeHash,
603636
host,
604637
)
605-
)
606-
);
638+
);
639+
});
607640
}
608641

609642
/**
610643
* handle dts and semantic diagnostics on file and iterate on anything that exports this file
644+
* return true when all work is done and we can exit handling dts emit and semantic diagnostics
611645
*/
612646
function handleDtsMayChangeOfFileAndExportsOfFile(
613647
state: BuilderProgramState,
@@ -616,9 +650,10 @@ namespace ts {
616650
cancellationToken: CancellationToken | undefined,
617651
computeHash: BuilderState.ComputeHash,
618652
host: BuilderProgramHost,
619-
): void {
620-
if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return;
653+
): boolean | undefined {
654+
if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) return undefined;
621655

656+
if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, computeHash, host)) return true;
622657
handleDtsMayChangeOf(state, filePath, cancellationToken, computeHash, host);
623658
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
624659

@@ -645,6 +680,7 @@ namespace ts {
645680
host,
646681
)
647682
);
683+
return undefined;
648684
}
649685

650686
/**

tests/baselines/reference/tsc/incremental/change-to-type-that-gets-used-as-global-through-export-in-another-file-through-indirect-import.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,21 @@ export default 2;
168168

169169
Output::
170170
/lib/tsc -p src/project
171-
exitCode:: ExitStatus.Success
171+
src/project/class1.ts:1:7 - error TS2322: Type '1' is not assignable to type '2'.
172+
173+
1 const a: MagicNumber = 1;
174+
   ~
175+
176+
177+
Found 1 error in src/project/class1.ts:1
178+
179+
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated
172180

173181

182+
//// [/src/project/class1.d.ts]
183+
declare const a = 2;
184+
185+
174186
//// [/src/project/constants.d.ts]
175187
declare const _default: 2;
176188
export default _default;
@@ -185,7 +197,7 @@ exports["default"] = 2;
185197
//// [/src/project/reexport.d.ts] file written with same contents
186198
//// [/src/project/reexport.js] file written with same contents
187199
//// [/src/project/tsconfig.tsbuildinfo]
188-
{"program":{"fileNames":["../../lib/lib.d.ts","./class1.ts","./constants.ts","./reexport.ts","./types.d.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"4085502068-const a: MagicNumber = 1;\nconsole.log(a);","signature":"-4973073251-declare const a = 1;\r\n","affectsGlobalScope":true},{"version":"-2659799015-export default 2;","signature":"1573564507-declare const _default: 2;\r\nexport default _default;\r\n"},{"version":"-1476032387-export { default as ConstantNumber } from \"./constants\"","signature":"-1329721329-export { default as ConstantNumber } from \"./constants\";\r\n"},{"version":"2093085814-type MagicNumber = typeof import('./reexport').ConstantNumber","affectsGlobalScope":true}],"options":{"composite":true},"fileIdsList":[[3],[4]],"referencedMap":[[4,1],[5,2]],"exportedModulesMap":[[4,1],[5,2]],"semanticDiagnosticsPerFile":[1,2,3,4,5]},"version":"FakeTSVersion"}
200+
{"program":{"fileNames":["../../lib/lib.d.ts","./class1.ts","./constants.ts","./reexport.ts","./types.d.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"4085502068-const a: MagicNumber = 1;\nconsole.log(a);","signature":"-4973037314-declare const a = 2;\r\n","affectsGlobalScope":true},{"version":"-2659799015-export default 2;","signature":"1573564507-declare const _default: 2;\r\nexport default _default;\r\n"},{"version":"-1476032387-export { default as ConstantNumber } from \"./constants\"","signature":"-1329721329-export { default as ConstantNumber } from \"./constants\";\r\n"},{"version":"2093085814-type MagicNumber = typeof import('./reexport').ConstantNumber","affectsGlobalScope":true}],"options":{"composite":true},"fileIdsList":[[3],[4]],"referencedMap":[[4,1],[5,2]],"exportedModulesMap":[[4,1],[5,2]],"semanticDiagnosticsPerFile":[1,[2,[{"file":"./class1.ts","start":6,"length":1,"code":2322,"category":1,"messageText":"Type '1' is not assignable to type '2'."}]],3,4,5]},"version":"FakeTSVersion"}
189201

190202
//// [/src/project/tsconfig.tsbuildinfo.readable.baseline.txt]
191203
{
@@ -213,7 +225,7 @@ exports["default"] = 2;
213225
},
214226
"./class1.ts": {
215227
"version": "4085502068-const a: MagicNumber = 1;\nconsole.log(a);",
216-
"signature": "-4973073251-declare const a = 1;\r\n",
228+
"signature": "-4973037314-declare const a = 2;\r\n",
217229
"affectsGlobalScope": true
218230
},
219231
"./constants.ts": {
@@ -251,13 +263,25 @@ exports["default"] = 2;
251263
},
252264
"semanticDiagnosticsPerFile": [
253265
"../../lib/lib.d.ts",
254-
"./class1.ts",
266+
[
267+
"./class1.ts",
268+
[
269+
{
270+
"file": "./class1.ts",
271+
"start": 6,
272+
"length": 1,
273+
"code": 2322,
274+
"category": 1,
275+
"messageText": "Type '1' is not assignable to type '2'."
276+
}
277+
]
278+
],
255279
"./constants.ts",
256280
"./reexport.ts",
257281
"./types.d.ts"
258282
]
259283
},
260284
"version": "FakeTSVersion",
261-
"size": 1347
285+
"size": 1476
262286
}
263287

tests/baselines/reference/tsc/incremental/change-to-type-that-gets-used-as-global-through-export-in-another-file.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,21 @@ export default 2;
127127

128128
Output::
129129
/lib/tsc -p src/project
130-
exitCode:: ExitStatus.Success
130+
src/project/class1.ts:1:7 - error TS2322: Type '1' is not assignable to type '2'.
131+
132+
1 const a: MagicNumber = 1;
133+
   ~
134+
135+
136+
Found 1 error in src/project/class1.ts:1
137+
138+
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated
131139

132140

141+
//// [/src/project/class1.d.ts]
142+
declare const a = 2;
143+
144+
133145
//// [/src/project/constants.d.ts]
134146
declare const _default: 2;
135147
export default _default;
@@ -142,7 +154,7 @@ exports["default"] = 2;
142154

143155

144156
//// [/src/project/tsconfig.tsbuildinfo]
145-
{"program":{"fileNames":["../../lib/lib.d.ts","./class1.ts","./constants.ts","./types.d.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"4085502068-const a: MagicNumber = 1;\nconsole.log(a);","signature":"-4973073251-declare const a = 1;\r\n","affectsGlobalScope":true},{"version":"-2659799015-export default 2;","signature":"1573564507-declare const _default: 2;\r\nexport default _default;\r\n"},{"version":"-2080821236-type MagicNumber = typeof import('./constants').default","affectsGlobalScope":true}],"options":{"composite":true},"fileIdsList":[[3]],"referencedMap":[[4,1]],"exportedModulesMap":[[4,1]],"semanticDiagnosticsPerFile":[1,2,3,4]},"version":"FakeTSVersion"}
157+
{"program":{"fileNames":["../../lib/lib.d.ts","./class1.ts","./constants.ts","./types.d.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"4085502068-const a: MagicNumber = 1;\nconsole.log(a);","signature":"-4973037314-declare const a = 2;\r\n","affectsGlobalScope":true},{"version":"-2659799015-export default 2;","signature":"1573564507-declare const _default: 2;\r\nexport default _default;\r\n"},{"version":"-2080821236-type MagicNumber = typeof import('./constants').default","affectsGlobalScope":true}],"options":{"composite":true},"fileIdsList":[[3]],"referencedMap":[[4,1]],"exportedModulesMap":[[4,1]],"semanticDiagnosticsPerFile":[1,[2,[{"file":"./class1.ts","start":6,"length":1,"code":2322,"category":1,"messageText":"Type '1' is not assignable to type '2'."}]],3,4]},"version":"FakeTSVersion"}
146158

147159
//// [/src/project/tsconfig.tsbuildinfo.readable.baseline.txt]
148160
{
@@ -166,7 +178,7 @@ exports["default"] = 2;
166178
},
167179
"./class1.ts": {
168180
"version": "4085502068-const a: MagicNumber = 1;\nconsole.log(a);",
169-
"signature": "-4973073251-declare const a = 1;\r\n",
181+
"signature": "-4973037314-declare const a = 2;\r\n",
170182
"affectsGlobalScope": true
171183
},
172184
"./constants.ts": {
@@ -194,12 +206,24 @@ exports["default"] = 2;
194206
},
195207
"semanticDiagnosticsPerFile": [
196208
"../../lib/lib.d.ts",
197-
"./class1.ts",
209+
[
210+
"./class1.ts",
211+
[
212+
{
213+
"file": "./class1.ts",
214+
"start": 6,
215+
"length": 1,
216+
"code": 2322,
217+
"category": 1,
218+
"messageText": "Type '1' is not assignable to type '2'."
219+
}
220+
]
221+
],
198222
"./constants.ts",
199223
"./types.d.ts"
200224
]
201225
},
202226
"version": "FakeTSVersion",
203-
"size": 1135
227+
"size": 1264
204228
}
205229

0 commit comments

Comments
 (0)