Skip to content

Commit e471856

Browse files
authored
Merge pull request #27082 from Microsoft/watchAPIAndProjectReferences
Fix tsc watch and watch API for a project that has project references
2 parents 1d22e03 + c63d581 commit e471856

File tree

13 files changed

+106
-55
lines changed

13 files changed

+106
-55
lines changed

src/compiler/builder.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ namespace ts {
294294
configFileParsingDiagnostics: ReadonlyArray<Diagnostic>;
295295
}
296296

297-
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderCreationParameters {
297+
export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: BuilderProgram | CompilerHost, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderCreationParameters {
298298
let host: BuilderProgramHost;
299299
let newProgram: Program;
300300
let oldProgram: BuilderProgram;
@@ -307,7 +307,14 @@ namespace ts {
307307
}
308308
else if (isArray(newProgramOrRootNames)) {
309309
oldProgram = configFileParsingDiagnosticsOrOldProgram as BuilderProgram;
310-
newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram(), configFileParsingDiagnostics);
310+
newProgram = createProgram({
311+
rootNames: newProgramOrRootNames,
312+
options: hostOrOptions as CompilerOptions,
313+
host: oldProgramOrHost as CompilerHost,
314+
oldProgram: oldProgram && oldProgram.getProgram(),
315+
configFileParsingDiagnostics,
316+
projectReferences
317+
});
311318
host = oldProgramOrHost as CompilerHost;
312319
}
313320
else {
@@ -623,28 +630,28 @@ namespace ts {
623630
* Create the builder to manage semantic diagnostics and cache them
624631
*/
625632
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
626-
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): SemanticDiagnosticsBuilderProgram;
627-
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
628-
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
633+
export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): SemanticDiagnosticsBuilderProgram;
634+
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>) {
635+
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
629636
}
630637

631638
/**
632639
* Create the builder that can handle the changes in program and iterate through changed files
633640
* to emit the those files and manage semantic diagnostics cache as well
634641
*/
635642
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
636-
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): EmitAndSemanticDiagnosticsBuilderProgram;
637-
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
638-
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics));
643+
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): EmitAndSemanticDiagnosticsBuilderProgram;
644+
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>) {
645+
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
639646
}
640647

641648
/**
642649
* Creates a builder thats just abstraction over program and can be used with watch
643650
*/
644651
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
645-
export function createAbstractBuilder(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram;
646-
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>): BuilderProgram {
647-
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics);
652+
export function createAbstractBuilder(rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderProgram;
653+
export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray<string> | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: ReadonlyArray<Diagnostic> | BuilderProgram, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>, projectReferences?: ReadonlyArray<ProjectReference>): BuilderProgram {
654+
const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
648655
return {
649656
// Only return program, all other methods are not implemented
650657
getProgram: () => program,

src/compiler/program.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ namespace ts {
442442
fileExists: (fileName: string) => boolean,
443443
hasInvalidatedResolution: HasInvalidatedResolution,
444444
hasChangedAutomaticTypeDirectiveNames: boolean,
445+
projectReferences: ReadonlyArray<ProjectReference> | undefined
445446
): boolean {
446447
// If we haven't created a program yet or have changed automatic type directives, then it is not up-to-date
447448
if (!program || hasChangedAutomaticTypeDirectiveNames) {
@@ -453,6 +454,11 @@ namespace ts {
453454
return false;
454455
}
455456

457+
// If project references dont match
458+
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences)) {
459+
return false;
460+
}
461+
456462
// If any file is not up-to-date, then the whole program is not up-to-date
457463
if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
458464
return false;
@@ -759,7 +765,8 @@ namespace ts {
759765
isEmittedFile,
760766
getConfigFileParsingDiagnostics,
761767
getResolvedModuleWithFailedLookupLocationsFromCache,
762-
getProjectReferences
768+
getProjectReferences,
769+
getResolvedProjectReferences
763770
};
764771

765772
verifyCompilerOptions();
@@ -1007,32 +1014,38 @@ namespace ts {
10071014
}
10081015

10091016
// Check if any referenced project tsconfig files are different
1010-
const oldRefs = oldProgram.getProjectReferences();
1017+
1018+
// If array of references is changed, we cant resue old program
1019+
const oldProjectReferences = oldProgram.getProjectReferences();
1020+
if (!arrayIsEqualTo(oldProjectReferences!, projectReferences, projectReferenceIsEqualTo)) {
1021+
return oldProgram.structureIsReused = StructureIsReused.Not;
1022+
}
1023+
1024+
// Check the json files for the project references
1025+
const oldRefs = oldProgram.getResolvedProjectReferences();
10111026
if (projectReferences) {
1012-
if (!oldRefs) {
1013-
return oldProgram.structureIsReused = StructureIsReused.Not;
1014-
}
1027+
// Resolved project referenced should be array if projectReferences provided are array
1028+
Debug.assert(!!oldRefs);
10151029
for (let i = 0; i < projectReferences.length; i++) {
1016-
const oldRef = oldRefs[i];
1030+
const oldRef = oldRefs![i];
1031+
const newRef = parseProjectReferenceConfigFile(projectReferences[i]);
10171032
if (oldRef) {
1018-
const newRef = parseProjectReferenceConfigFile(projectReferences[i]);
10191033
if (!newRef || newRef.sourceFile !== oldRef.sourceFile) {
10201034
// Resolved project reference has gone missing or changed
10211035
return oldProgram.structureIsReused = StructureIsReused.Not;
10221036
}
10231037
}
10241038
else {
10251039
// A previously-unresolved reference may be resolved now
1026-
if (parseProjectReferenceConfigFile(projectReferences[i]) !== undefined) {
1040+
if (newRef !== undefined) {
10271041
return oldProgram.structureIsReused = StructureIsReused.Not;
10281042
}
10291043
}
10301044
}
10311045
}
10321046
else {
1033-
if (oldRefs) {
1034-
return oldProgram.structureIsReused = StructureIsReused.Not;
1035-
}
1047+
// Resolved project referenced should be undefined if projectReferences is undefined
1048+
Debug.assert(!oldRefs);
10361049
}
10371050

10381051
// check if program source files has changed in the way that can affect structure of the program
@@ -1219,7 +1232,7 @@ namespace ts {
12191232
fileProcessingDiagnostics.reattachFileDiagnostics(modifiedFile.newFile);
12201233
}
12211234
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
1222-
resolvedProjectReferences = oldProgram.getProjectReferences();
1235+
resolvedProjectReferences = oldProgram.getResolvedProjectReferences();
12231236

12241237
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
12251238
redirectTargetsMap = oldProgram.redirectTargetsMap;
@@ -1257,10 +1270,14 @@ namespace ts {
12571270
};
12581271
}
12591272

1260-
function getProjectReferences() {
1273+
function getResolvedProjectReferences() {
12611274
return resolvedProjectReferences;
12621275
}
12631276

1277+
function getProjectReferences() {
1278+
return projectReferences;
1279+
}
1280+
12641281
function getPrependNodes(): InputFiles[] {
12651282
if (!projectReferences) {
12661283
return emptyArray;

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2815,7 +2815,8 @@ namespace ts {
28152815

28162816
/* @internal */ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;
28172817

2818-
getProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
2818+
getProjectReferences(): ReadonlyArray<ProjectReference> | undefined;
2819+
getResolvedProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
28192820
}
28202821

28212822
/* @internal */

src/compiler/utilities.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ namespace ts {
249249
sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, resolvedTypeReferenceDirective);
250250
}
251251

252+
export function projectReferenceIsEqualTo(oldRef: ProjectReference, newRef: ProjectReference) {
253+
return oldRef.path === newRef.path &&
254+
!oldRef.prepend === !newRef.prepend &&
255+
!oldRef.circular === !newRef.circular;
256+
}
257+
252258
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
253259
return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
254260
oldResolution.extension === newResolution.extension &&

0 commit comments

Comments
 (0)