Skip to content

Commit e3026b0

Browse files
authored
Merge pull request #27155 from Microsoft/fixIncrementalWatchProjectWithReferences
Fix incremental watch when project built has project references
2 parents cfd0a62 + a5fd3e9 commit e3026b0

File tree

8 files changed

+190
-35
lines changed

8 files changed

+190
-35
lines changed

src/compiler/builderState.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ namespace ts.BuilderState {
8888
function getReferencedFileFromImportedModuleSymbol(symbol: Symbol) {
8989
if (symbol.declarations && symbol.declarations[0]) {
9090
const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]);
91-
return declarationSourceFile && declarationSourceFile.path;
91+
return declarationSourceFile && (declarationSourceFile.resolvedPath || declarationSourceFile.path);
9292
}
9393
}
9494

@@ -100,6 +100,13 @@ namespace ts.BuilderState {
100100
return symbol && getReferencedFileFromImportedModuleSymbol(symbol);
101101
}
102102

103+
/**
104+
* Gets the path to reference file from file name, it could be resolvedPath if present otherwise path
105+
*/
106+
function getReferencedFileFromFileName(program: Program, fileName: string, sourceFileDirectory: Path, getCanonicalFileName: GetCanonicalFileName): Path {
107+
return toPath(program.getProjectReferenceRedirect(fileName) || fileName, sourceFileDirectory, getCanonicalFileName);
108+
}
109+
103110
/**
104111
* Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true
105112
*/
@@ -123,7 +130,7 @@ namespace ts.BuilderState {
123130
// Handle triple slash references
124131
if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) {
125132
for (const referencedFile of sourceFile.referencedFiles) {
126-
const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
133+
const referencedPath = getReferencedFileFromFileName(program, referencedFile.fileName, sourceFileDirectory, getCanonicalFileName);
127134
addReferencedFile(referencedPath);
128135
}
129136
}
@@ -136,7 +143,7 @@ namespace ts.BuilderState {
136143
}
137144

138145
const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217
139-
const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName);
146+
const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName);
140147
addReferencedFile(typeFilePath);
141148
});
142149
}

src/compiler/core.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ namespace ts {
842842
return deduplicateSorted(sort(array, comparer), equalityComparer || comparer);
843843
}
844844

845-
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T> | undefined, array2: ReadonlyArray<T> | undefined, equalityComparer: (a: T, b: T) => boolean = equateValues): boolean {
845+
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T> | undefined, array2: ReadonlyArray<T> | undefined, equalityComparer: (a: T, b: T, index: number) => boolean = equateValues): boolean {
846846
if (!array1 || !array2) {
847847
return array1 === array2;
848848
}
@@ -852,7 +852,7 @@ namespace ts {
852852
}
853853

854854
for (let i = 0; i < array1.length; i++) {
855-
if (!equalityComparer(array1[i], array2[i])) {
855+
if (!equalityComparer(array1[i], array2[i], i)) {
856856
return false;
857857
}
858858
}

src/compiler/program.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ namespace ts {
455455
}
456456

457457
// If project references dont match
458-
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences)) {
458+
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences, projectReferenceUptoDate)) {
459459
return false;
460460
}
461461

@@ -483,9 +483,27 @@ namespace ts {
483483

484484
return true;
485485

486-
function sourceFileNotUptoDate(sourceFile: SourceFile): boolean {
487-
return sourceFile.version !== getSourceVersion(sourceFile.path) ||
488-
hasInvalidatedResolution(sourceFile.path);
486+
function sourceFileNotUptoDate(sourceFile: SourceFile) {
487+
return !sourceFileVersionUptoDate(sourceFile) ||
488+
hasInvalidatedResolution(sourceFile.resolvedPath || sourceFile.path);
489+
}
490+
491+
function sourceFileVersionUptoDate(sourceFile: SourceFile) {
492+
return sourceFile.version === getSourceVersion(sourceFile.resolvedPath || sourceFile.path);
493+
}
494+
495+
function projectReferenceUptoDate(oldRef: ProjectReference, newRef: ProjectReference, index: number) {
496+
if (!projectReferenceIsEqualTo(oldRef, newRef)) {
497+
return false;
498+
}
499+
const oldResolvedRef = program!.getResolvedProjectReferences()![index];
500+
if (oldResolvedRef) {
501+
// If sourceFile for the oldResolvedRef existed, check the version for uptodate
502+
return sourceFileVersionUptoDate(oldResolvedRef.sourceFile);
503+
}
504+
// In old program, not able to resolve project reference path,
505+
// so if config file doesnt exist, it is uptodate.
506+
return !fileExists(resolveProjectReferencePath(oldRef));
489507
}
490508
}
491509

@@ -658,8 +676,9 @@ namespace ts {
658676
const parsedRef = parseProjectReferenceConfigFile(ref);
659677
resolvedProjectReferences!.push(parsedRef);
660678
if (parsedRef) {
661-
if (parsedRef.commandLine.options.outFile) {
662-
const dtsOutfile = changeExtension(parsedRef.commandLine.options.outFile, ".d.ts");
679+
const out = parsedRef.commandLine.options.outFile || parsedRef.commandLine.options.out;
680+
if (out) {
681+
const dtsOutfile = changeExtension(out, ".d.ts");
663682
processSourceFile(dtsOutfile, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined);
664683
}
665684
addProjectReferenceRedirects(parsedRef.commandLine, projectReferenceRedirects);
@@ -758,7 +777,8 @@ namespace ts {
758777
getConfigFileParsingDiagnostics,
759778
getResolvedModuleWithFailedLookupLocationsFromCache,
760779
getProjectReferences,
761-
getResolvedProjectReferences
780+
getResolvedProjectReferences,
781+
getProjectReferenceRedirect
762782
};
763783

764784
verifyCompilerOptions();
@@ -1225,6 +1245,13 @@ namespace ts {
12251245
}
12261246
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
12271247
resolvedProjectReferences = oldProgram.getResolvedProjectReferences();
1248+
if (resolvedProjectReferences) {
1249+
resolvedProjectReferences.forEach(ref => {
1250+
if (ref) {
1251+
addProjectReferenceRedirects(ref.commandLine, projectReferenceRedirects);
1252+
}
1253+
});
1254+
}
12281255

12291256
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
12301257
redirectTargetsMap = oldProgram.redirectTargetsMap;
@@ -1280,12 +1307,13 @@ namespace ts {
12801307
const ref = projectReferences[i];
12811308
const resolvedRefOpts = resolvedProjectReferences![i]!.commandLine;
12821309
if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) {
1310+
const out = resolvedRefOpts.options.outFile || resolvedRefOpts.options.out;
12831311
// Upstream project didn't have outFile set -- skip (error will have been issued earlier)
1284-
if (!resolvedRefOpts.options.outFile) continue;
1312+
if (!out) continue;
12851313

1286-
const dtsFilename = changeExtension(resolvedRefOpts.options.outFile, ".d.ts");
1287-
const js = host.readFile(resolvedRefOpts.options.outFile) || `/* Input file ${resolvedRefOpts.options.outFile} was missing */\r\n`;
1288-
const jsMapPath = resolvedRefOpts.options.outFile + ".map"; // TODO: try to read sourceMappingUrl comment from the file
1314+
const dtsFilename = changeExtension(out, ".d.ts");
1315+
const js = host.readFile(out) || `/* Input file ${out} was missing */\r\n`;
1316+
const jsMapPath = out + ".map"; // TODO: try to read sourceMappingUrl comment from the file
12891317
const jsMap = host.readFile(jsMapPath);
12901318
const dts = host.readFile(dtsFilename) || `/* Input file ${dtsFilename} was missing */\r\n`;
12911319
const dtsMapPath = dtsFilename + ".map";
@@ -2427,9 +2455,10 @@ namespace ts {
24272455
createDiagnosticForReference(i, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
24282456
}
24292457
if (ref.prepend) {
2430-
if (resolvedRefOpts.outFile) {
2431-
if (!host.fileExists(resolvedRefOpts.outFile)) {
2432-
createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, resolvedRefOpts.outFile, ref.path);
2458+
const out = resolvedRefOpts.outFile || resolvedRefOpts.out;
2459+
if (out) {
2460+
if (!host.fileExists(out)) {
2461+
createDiagnosticForReference(i, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
24332462
}
24342463
}
24352464
else {

src/compiler/tsbuild.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,16 +285,17 @@ namespace ts {
285285
}
286286

287287
function getOutFileOutputs(project: ParsedCommandLine): ReadonlyArray<string> {
288-
if (!project.options.outFile) {
288+
const out = project.options.outFile || project.options.out;
289+
if (!out) {
289290
return Debug.fail("outFile must be set");
290291
}
291292
const outputs: string[] = [];
292-
outputs.push(project.options.outFile);
293+
outputs.push(out);
293294
if (project.options.sourceMap) {
294-
outputs.push(`${project.options.outFile}.map`);
295+
outputs.push(`${out}.map`);
295296
}
296297
if (getEmitDeclarations(project.options)) {
297-
const dts = changeExtension(project.options.outFile, Extension.Dts);
298+
const dts = changeExtension(out, Extension.Dts);
298299
outputs.push(dts);
299300
if (project.options.declarationMap) {
300301
outputs.push(`${dts}.map`);
@@ -862,7 +863,7 @@ namespace ts {
862863
if (buildProject) {
863864
buildSingleInvalidatedProject(buildProject.project, buildProject.reloadLevel);
864865
if (hasPendingInvalidatedProjects()) {
865-
if (!timerToBuildInvalidatedProject) {
866+
if (options.watch && !timerToBuildInvalidatedProject) {
866867
scheduleBuildInvalidatedProject();
867868
}
868869
}
@@ -1248,7 +1249,7 @@ namespace ts {
12481249
}
12491250

12501251
export function getAllProjectOutputs(project: ParsedCommandLine): ReadonlyArray<string> {
1251-
if (project.options.outFile) {
1252+
if (project.options.outFile || project.options.out) {
12521253
return getOutFileOutputs(project);
12531254
}
12541255
else {

src/compiler/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,6 +2551,11 @@ namespace ts {
25512551
fileName: string;
25522552
/* @internal */ path: Path;
25532553
text: string;
2554+
/** Resolved path can be different from path property,
2555+
* when file is included through project reference is mapped to its output instead of source
2556+
* in that case resolvedPath = path to output file
2557+
* path = input file's path
2558+
*/
25542559
/* @internal */ resolvedPath: Path;
25552560

25562561
/**
@@ -2819,6 +2824,7 @@ namespace ts {
28192824

28202825
getProjectReferences(): ReadonlyArray<ProjectReference> | undefined;
28212826
getResolvedProjectReferences(): (ResolvedProjectReference | undefined)[] | undefined;
2827+
/*@internal*/ getProjectReferenceRedirect(fileName: string): string | undefined;
28222828
}
28232829

28242830
/* @internal */

src/harness/virtualFileSystemWithWatch.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,10 @@ interface Array<T> {}`
187187
}
188188

189189
export function checkArray(caption: string, actual: ReadonlyArray<string>, expected: ReadonlyArray<string>) {
190+
checkMapKeys(caption, arrayToMap(actual, identity), expected);
190191
assert.equal(actual.length, expected.length, `${caption}: incorrect actual number of files, expected:\r\n${expected.join("\r\n")}\r\ngot: ${actual.join("\r\n")}`);
191192
for (const f of expected) {
192-
assert.equal(true, contains(actual, f), `${caption}: expected to find ${f} in ${actual}`);
193+
assert.isTrue(contains(actual, f), `${caption}: expected to find ${f} in ${actual}`);
193194
}
194195
}
195196

@@ -934,7 +935,12 @@ interface Array<T> {}`
934935
const folder = this.fs.get(base) as FsFolder;
935936
Debug.assert(isFsFolder(folder));
936937

937-
this.addFileOrFolderInFolder(folder, file);
938+
if (!this.fs.has(file.path)) {
939+
this.addFileOrFolderInFolder(folder, file);
940+
}
941+
else {
942+
this.modifyFile(path, content);
943+
}
938944
}
939945

940946
write(message: string) {

0 commit comments

Comments
 (0)