From 62f980aff82ac8b5939bc9b65cf6e55d122b42d9 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 2 Sep 2022 13:38:00 -0700 Subject: [PATCH] Check if its same buildinfo only for directly referenced projects and not recursively (#50617) Fixes #50545 --- src/compiler/tsbuildPublic.ts | 22 +- .../tsbuild/containerOnlyReferenced.ts | 31 ++ .../when-solution-is-referenced-indirectly.js | 303 ++++++++++++++++++ 3 files changed, 337 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/tsbuild/containerOnlyReferenced/when-solution-is-referenced-indirectly.js diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 2b9d97f324ee2..0849c5921f29a 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1706,10 +1706,7 @@ namespace ts { } } - const seenRefs = buildInfoPath ? new Set() : undefined; const buildInfoCacheEntry = state.buildInfoCache.get(resolvedPath); - seenRefs?.add(resolvedPath); - /** Inputs are up-to-date, just need either timestamp update or bundle prepend manipulation to make it look up-to-date */ let pseudoUpToDate = false; let usesPrepend = false; @@ -1724,7 +1721,7 @@ namespace ts { } // Check if tsbuildinfo path is shared, then we need to rebuild - if (buildInfoCacheEntry && hasSameBuildInfo(state, buildInfoCacheEntry, seenRefs!, resolvedConfig, resolvedRefPath)) { + if (buildInfoCacheEntry && hasSameBuildInfo(state, buildInfoCacheEntry, resolvedRefPath)) { return { type: UpToDateStatusType.OutOfDateWithUpstream, outOfDateOutputFileName: buildInfoPath!, @@ -1787,22 +1784,9 @@ namespace ts { }; } - function hasSameBuildInfo(state: SolutionBuilderState, buildInfoCacheEntry: BuildInfoCacheEntry, seenRefs: Set, resolvedConfig: ParsedCommandLine, resolvedRefPath: ResolvedConfigFilePath) { - if (seenRefs.has(resolvedRefPath)) return false; - seenRefs.add(resolvedRefPath); + function hasSameBuildInfo(state: SolutionBuilderState, buildInfoCacheEntry: BuildInfoCacheEntry, resolvedRefPath: ResolvedConfigFilePath) { const refBuildInfo = state.buildInfoCache.get(resolvedRefPath)!; - if (refBuildInfo.path === buildInfoCacheEntry.path) return true; - - if (resolvedConfig.projectReferences) { - // Check references - for (const ref of resolvedConfig.projectReferences) { - const resolvedRef = resolveProjectReferencePath(ref); - const resolvedRefPath = toResolvedConfigFilePath(state, resolvedRef); - const resolvedConfig = parseConfigFile(state, resolvedRef, resolvedRefPath)!; - if (hasSameBuildInfo(state, buildInfoCacheEntry, seenRefs, resolvedConfig, resolvedRefPath)) return true; - } - } - return false; + return refBuildInfo.path === buildInfoCacheEntry.path; } function getUpToDateStatus(state: SolutionBuilderState, project: ParsedCommandLine | undefined, resolvedPath: ResolvedConfigFilePath): UpToDateStatus { diff --git a/src/testRunner/unittests/tsbuild/containerOnlyReferenced.ts b/src/testRunner/unittests/tsbuild/containerOnlyReferenced.ts index e9eacf1fa657c..595bc509744d5 100644 --- a/src/testRunner/unittests/tsbuild/containerOnlyReferenced.ts +++ b/src/testRunner/unittests/tsbuild/containerOnlyReferenced.ts @@ -7,5 +7,36 @@ namespace ts { commandLineArgs: ["--b", "/src", "--verbose"], edits: noChangeOnlyRuns }); + + verifyTscWithEdits({ + scenario: "containerOnlyReferenced", + subScenario: "when solution is referenced indirectly", + fs: () => loadProjectFromFiles({ + "/src/project1/tsconfig.json": JSON.stringify({ + compilerOptions: { composite: true }, + references: [], + }), + "/src/project2/tsconfig.json": JSON.stringify({ + compilerOptions: { composite: true }, + references: [], + }), + "/src/project2/src/b.ts": "export const b = 10;", + "/src/project3/tsconfig.json": JSON.stringify({ + compilerOptions: { composite: true }, + references: [{ path: "../project1", }, { path: "../project2" }], + }), + "/src/project3/src/c.ts": "export const c = 10;", + "/src/project4/tsconfig.json": JSON.stringify({ + compilerOptions: { composite: true }, + references: [{ path: "../project3" }] + }), + "/src/project4/src/d.ts": "export const d = 10;", + }), + commandLineArgs: ["--b", "/src/project4", "--verbose", "--explainFiles"], + edits: [{ + subScenario: "modify project3 file", + modifyFs: fs => replaceText(fs, "/src/project3/src/c.ts", "c = ", "cc = "), + }], + }); }); } diff --git a/tests/baselines/reference/tsbuild/containerOnlyReferenced/when-solution-is-referenced-indirectly.js b/tests/baselines/reference/tsbuild/containerOnlyReferenced/when-solution-is-referenced-indirectly.js new file mode 100644 index 0000000000000..7cc61e7f319ee --- /dev/null +++ b/tests/baselines/reference/tsbuild/containerOnlyReferenced/when-solution-is-referenced-indirectly.js @@ -0,0 +1,303 @@ +Input:: +//// [/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } +interface ReadonlyArray {} +declare const console: { log(msg: any): void; }; + +//// [/src/project1/tsconfig.json] +{"compilerOptions":{"composite":true},"references":[]} + +//// [/src/project2/src/b.ts] +export const b = 10; + +//// [/src/project2/tsconfig.json] +{"compilerOptions":{"composite":true},"references":[]} + +//// [/src/project3/src/c.ts] +export const c = 10; + +//// [/src/project3/tsconfig.json] +{"compilerOptions":{"composite":true},"references":[{"path":"../project1"},{"path":"../project2"}]} + +//// [/src/project4/src/d.ts] +export const d = 10; + +//// [/src/project4/tsconfig.json] +{"compilerOptions":{"composite":true},"references":[{"path":"../project3"}]} + + + +Output:: +/lib/tsc --b /src/project4 --verbose --explainFiles +[12:00:20 AM] Projects in this build: + * src/project1/tsconfig.json + * src/project2/tsconfig.json + * src/project3/tsconfig.json + * src/project4/tsconfig.json + +[12:00:21 AM] Project 'src/project2/tsconfig.json' is out of date because output file 'src/project2/tsconfig.tsbuildinfo' does not exist + +[12:00:22 AM] Building project '/src/project2/tsconfig.json'... + +lib/lib.d.ts + Default library for target 'es3' +src/project2/src/b.ts + Matched by default include pattern '**/*' +[12:00:28 AM] Project 'src/project3/tsconfig.json' is out of date because output file 'src/project3/tsconfig.tsbuildinfo' does not exist + +[12:00:29 AM] Building project '/src/project3/tsconfig.json'... + +lib/lib.d.ts + Default library for target 'es3' +src/project3/src/c.ts + Matched by default include pattern '**/*' +[12:00:35 AM] Project 'src/project4/tsconfig.json' is out of date because output file 'src/project4/tsconfig.tsbuildinfo' does not exist + +[12:00:36 AM] Building project '/src/project4/tsconfig.json'... + +lib/lib.d.ts + Default library for target 'es3' +src/project4/src/d.ts + Matched by default include pattern '**/*' +exitCode:: ExitStatus.Success + + +//// [/src/project2/src/b.d.ts] +export declare const b = 10; + + +//// [/src/project2/src/b.js] +"use strict"; +exports.__esModule = true; +exports.b = void 0; +exports.b = 10; + + +//// [/src/project2/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../lib/lib.d.ts","./src/b.ts"],"fileInfos":[{"version":"3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"-13368947479-export const b = 10;","signature":"-1807916688-export declare const b = 10;\r\n"}],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/b.d.ts"},"version":"FakeTSVersion"} + +//// [/src/project2/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../lib/lib.d.ts", + "./src/b.ts" + ], + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/b.ts": { + "version": "-13368947479-export const b = 10;", + "signature": "-1807916688-export declare const b = 10;\r\n" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./src/b.ts" + ], + "latestChangedDtsFile": "./src/b.d.ts" + }, + "version": "FakeTSVersion", + "size": 832 +} + +//// [/src/project3/src/c.d.ts] +export declare const c = 10; + + +//// [/src/project3/src/c.js] +"use strict"; +exports.__esModule = true; +exports.c = void 0; +exports.c = 10; + + +//// [/src/project3/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../lib/lib.d.ts","./src/c.ts"],"fileInfos":[{"version":"3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"-12077479510-export const c = 10;","signature":"-4148571535-export declare const c = 10;\r\n"}],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/c.d.ts"},"version":"FakeTSVersion"} + +//// [/src/project3/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../lib/lib.d.ts", + "./src/c.ts" + ], + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/c.ts": { + "version": "-12077479510-export const c = 10;", + "signature": "-4148571535-export declare const c = 10;\r\n" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./src/c.ts" + ], + "latestChangedDtsFile": "./src/c.d.ts" + }, + "version": "FakeTSVersion", + "size": 832 +} + +//// [/src/project4/src/d.d.ts] +export declare const d = 10; + + +//// [/src/project4/src/d.js] +"use strict"; +exports.__esModule = true; +exports.d = void 0; +exports.d = 10; + + +//// [/src/project4/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../lib/lib.d.ts","./src/d.ts"],"fileInfos":[{"version":"3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"-10786011541-export const d = 10;","signature":"-6489226382-export declare const d = 10;\r\n"}],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/d.d.ts"},"version":"FakeTSVersion"} + +//// [/src/project4/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../lib/lib.d.ts", + "./src/d.ts" + ], + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/d.ts": { + "version": "-10786011541-export const d = 10;", + "signature": "-6489226382-export declare const d = 10;\r\n" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./src/d.ts" + ], + "latestChangedDtsFile": "./src/d.d.ts" + }, + "version": "FakeTSVersion", + "size": 832 +} + + + +Change:: modify project3 file +Input:: +//// [/src/project3/src/c.ts] +export const cc = 10; + + + +Output:: +/lib/tsc --b /src/project4 --verbose --explainFiles +[12:00:43 AM] Projects in this build: + * src/project1/tsconfig.json + * src/project2/tsconfig.json + * src/project3/tsconfig.json + * src/project4/tsconfig.json + +[12:00:44 AM] Project 'src/project2/tsconfig.json' is up to date because newest input 'src/project2/src/b.ts' is older than output 'src/project2/tsconfig.tsbuildinfo' + +[12:00:45 AM] Project 'src/project3/tsconfig.json' is out of date because output 'src/project3/tsconfig.tsbuildinfo' is older than input 'src/project3/src/c.ts' + +[12:00:46 AM] Building project '/src/project3/tsconfig.json'... + +lib/lib.d.ts + Default library for target 'es3' +src/project3/src/c.ts + Matched by default include pattern '**/*' +[12:00:52 AM] Project 'src/project4/tsconfig.json' is out of date because output 'src/project4/tsconfig.tsbuildinfo' is older than input 'src/project3' + +[12:00:53 AM] Building project '/src/project4/tsconfig.json'... + +[12:00:54 AM] Updating unchanged output timestamps of project '/src/project4/tsconfig.json'... + +lib/lib.d.ts + Default library for target 'es3' +src/project4/src/d.ts + Matched by default include pattern '**/*' +exitCode:: ExitStatus.Success + + +//// [/src/project3/src/c.d.ts] +export declare const cc = 10; + + +//// [/src/project3/src/c.js] +"use strict"; +exports.__esModule = true; +exports.cc = void 0; +exports.cc = 10; + + +//// [/src/project3/tsconfig.tsbuildinfo] +{"program":{"fileNames":["../../lib/lib.d.ts","./src/c.ts"],"fileInfos":[{"version":"3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"-12481904019-export const cc = 10;","signature":"-2519819788-export declare const cc = 10;\r\n"}],"options":{"composite":true},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/c.d.ts"},"version":"FakeTSVersion"} + +//// [/src/project3/tsconfig.tsbuildinfo.readable.baseline.txt] +{ + "program": { + "fileNames": [ + "../../lib/lib.d.ts", + "./src/c.ts" + ], + "fileInfos": { + "../../lib/lib.d.ts": { + "version": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "signature": "3858781397-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ndeclare const console: { log(msg: any): void; };", + "affectsGlobalScope": true + }, + "./src/c.ts": { + "version": "-12481904019-export const cc = 10;", + "signature": "-2519819788-export declare const cc = 10;\r\n" + } + }, + "options": { + "composite": true + }, + "referencedMap": {}, + "exportedModulesMap": {}, + "semanticDiagnosticsPerFile": [ + "../../lib/lib.d.ts", + "./src/c.ts" + ], + "latestChangedDtsFile": "./src/c.d.ts" + }, + "version": "FakeTSVersion", + "size": 834 +} + +//// [/src/project4/tsconfig.tsbuildinfo] file changed its modified time