Skip to content

Commit db8c6ee

Browse files
committed
When exported types from module change, the modules exporting these types indirectly mean d.ts change too (not just semantic diagnostics)
1 parent 0be9a22 commit db8c6ee

File tree

2 files changed

+78
-36
lines changed

2 files changed

+78
-36
lines changed

src/compiler/builder.ts

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,7 @@ namespace ts {
241241
oldCompilerOptions.declarationDir !== compilerOptions.declarationDir ||
242242
(oldCompilerOptions.outFile || oldCompilerOptions.out) !== (compilerOptions.outFile || compilerOptions.out))) {
243243
// Add all files to affectedFilesPendingEmit since emit changed
244-
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, newProgram.getSourceFiles().map(f => f.path));
245-
if (state.affectedFilesPendingEmitIndex === undefined) {
246-
state.affectedFilesPendingEmitIndex = 0;
247-
}
244+
addToAffectedFilesPendingEmit(state, newProgram.getSourceFiles().map(f => f.path));
248245
Debug.assert(state.seenAffectedFiles === undefined);
249246
state.seenAffectedFiles = createMap<true>();
250247
}
@@ -343,6 +340,7 @@ namespace ts {
343340
// Set the next affected file as seen and remove the cached semantic diagnostics
344341
state.affectedFilesIndex = affectedFilesIndex;
345342
cleanSemanticDiagnosticsOfAffectedFile(state, affectedFile);
343+
handleDtsMayChangeOfAffectedFile(state, affectedFile)
346344
return affectedFile;
347345
}
348346
seenAffectedFiles.set(affectedFile.path, true);
@@ -433,7 +431,55 @@ namespace ts {
433431

434432
// If there was change in signature for the changed file,
435433
// then delete the semantic diagnostics for files that are affected by using exports of this module
434+
forEachReferencingModulesOfExportOfAffectedFile(state, affectedFile, removeSemanticDiagnosticsOf);
435+
}
436436

437+
/**
438+
* Removes semantic diagnostics for path and
439+
* returns true if there are no more semantic diagnostics from the old state
440+
*/
441+
function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
442+
if (!state.semanticDiagnosticsFromOldState) {
443+
return true;
444+
}
445+
state.semanticDiagnosticsFromOldState.delete(path);
446+
state.semanticDiagnosticsPerFile!.delete(path);
447+
return !state.semanticDiagnosticsFromOldState.size;
448+
}
449+
450+
/**
451+
* Add files, that are referencing modules that export entities from affected file as pending emit since dts may change
452+
* Similar to cleanSemanticDiagnosticsOfAffectedFile
453+
*/
454+
function handleDtsMayChangeOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile) {
455+
// If not dts emit, nothing more to do
456+
if (!getEmitDeclarations(state.compilerOptions)) {
457+
return;
458+
}
459+
460+
// If affected files is everything except default librarry, then nothing more to do
461+
if (state.allFilesExcludingDefaultLibraryFile === state.affectedFiles) {
462+
return;
463+
}
464+
465+
// If there was change in signature (dts output) for the changed file,
466+
// then only we need to handle pending file emit
467+
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
468+
return;
469+
}
470+
471+
forEachReferencingModulesOfExportOfAffectedFile(state, affectedFile, (state, filePath) => {
472+
addToAffectedFilesPendingEmit(state, [filePath]);
473+
return false;
474+
});
475+
}
476+
477+
/**
478+
* Iterate on referencing modules that export entities from affected file
479+
*/
480+
function forEachReferencingModulesOfExportOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile, fn: (state: BuilderProgramState, filePath: Path) => boolean) {
481+
// If there was change in signature (dts output) for the changed file,
482+
// then only we need to handle pending file emit
437483
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
438484
return;
439485
}
@@ -445,7 +491,7 @@ namespace ts {
445491
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
446492
exportedModules &&
447493
exportedModules.has(affectedFile.path) &&
448-
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
494+
forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
449495
)) {
450496
return;
451497
}
@@ -454,29 +500,28 @@ namespace ts {
454500
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) =>
455501
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
456502
exportedModules.has(affectedFile.path) &&
457-
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile)
503+
forEachFilesReferencingPath(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
458504
);
459505
}
460506

461507
/**
462-
* removes the semantic diagnostics of files referencing referencedPath and
463-
* returns true if there are no more semantic diagnostics from old state
508+
* Iterate on files referencing referencedPath
464509
*/
465-
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map<true>) {
510+
function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Map<true>, fn: (state: BuilderProgramState, filePath: Path) => boolean) {
466511
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
467-
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOfFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile)
512+
referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath as Path, seenFileAndExportsOfFile, fn)
468513
);
469514
}
470515

471516
/**
472-
* Removes semantic diagnostics of file and anything that exports this file
517+
* fn on file and iterate on anything that exports this file
473518
*/
474-
function removeSemanticDiagnosticsOfFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map<true>): boolean {
519+
function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Map<true>, fn: (state: BuilderProgramState, filePath: Path) => boolean): boolean {
475520
if (!addToSeen(seenFileAndExportsOfFile, filePath)) {
476521
return false;
477522
}
478523

479-
if (removeSemanticDiagnosticsOf(state, filePath)) {
524+
if (fn(state, filePath)) {
480525
// If there are no more diagnostics from old cache, done
481526
return true;
482527
}
@@ -487,7 +532,7 @@ namespace ts {
487532
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
488533
exportedModules &&
489534
exportedModules.has(filePath) &&
490-
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
535+
forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
491536
)) {
492537
return true;
493538
}
@@ -496,7 +541,7 @@ namespace ts {
496541
if (forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
497542
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
498543
exportedModules.has(filePath) &&
499-
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile)
544+
forEachFileAndExportsOfFile(state, exportedFromPath as Path, seenFileAndExportsOfFile, fn)
500545
)) {
501546
return true;
502547
}
@@ -505,22 +550,10 @@ namespace ts {
505550
return !!forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) =>
506551
referencesInFile.has(filePath) &&
507552
!seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file
508-
removeSemanticDiagnosticsOf(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
553+
fn(state, referencingFilePath as Path) // Dont add to seen since this is not yet done with the export removal
509554
);
510555
}
511556

512-
/**
513-
* Removes semantic diagnostics for path and
514-
* returns true if there are no more semantic diagnostics from the old state
515-
*/
516-
function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
517-
if (!state.semanticDiagnosticsFromOldState) {
518-
return true;
519-
}
520-
state.semanticDiagnosticsFromOldState.delete(path);
521-
state.semanticDiagnosticsPerFile!.delete(path);
522-
return !state.semanticDiagnosticsFromOldState.size;
523-
}
524557

525558
/**
526559
* This is called after completing operation on the next affected file.
@@ -929,14 +962,7 @@ namespace ts {
929962

930963
// In case of emit builder, cache the files to be emitted
931964
if (affectedFilesPendingEmit) {
932-
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
933-
// affectedFilesPendingEmitIndex === undefined
934-
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
935-
// so start from 0 as array would be affectedFilesPendingEmit
936-
// else, continue to iterate from existing index, the current set is appended to existing files
937-
if (state.affectedFilesPendingEmitIndex === undefined) {
938-
state.affectedFilesPendingEmitIndex = 0;
939-
}
965+
addToAffectedFilesPendingEmit(state, affectedFilesPendingEmit);
940966
}
941967

942968
let diagnostics: Diagnostic[] | undefined;
@@ -947,6 +973,17 @@ namespace ts {
947973
}
948974
}
949975

976+
function addToAffectedFilesPendingEmit(state: BuilderProgramState, affectedFilesPendingEmit: readonly Path[]) {
977+
state.affectedFilesPendingEmit = concatenate(state.affectedFilesPendingEmit, affectedFilesPendingEmit);
978+
// affectedFilesPendingEmitIndex === undefined
979+
// - means the emit state.affectedFilesPendingEmit was undefined before adding current affected files
980+
// so start from 0 as array would be affectedFilesPendingEmit
981+
// else, continue to iterate from existing index, the current set is appended to existing files
982+
if (state.affectedFilesPendingEmitIndex === undefined) {
983+
state.affectedFilesPendingEmitIndex = 0;
984+
}
985+
}
986+
950987
function getMapOfReferencedSet(mapLike: MapLike<ReadonlyArray<string>> | undefined): ReadonlyMap<BuilderState.ReferencedSet> | undefined {
951988
if (!mapLike) return undefined;
952989
const map = createMap<BuilderState.ReferencedSet>();

tests/baselines/reference/tsbuild/inferredTypeFromTransitiveModule/incremental-declaration-changes/inferred-type-from-transitive-module.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ exports.default = foo()(function foobar() {
2121
});
2222

2323

24+
//// [/src/obj/index.d.ts]
25+
import { LazyAction } from './bundling';
26+
export declare const lazyBar: LazyAction<() => void, typeof import("./lazyIndex")>;
27+
28+
2429
//// [/src/obj/tsconfig.tsbuildinfo]
2530
{
2631
"program": {

0 commit comments

Comments
 (0)