diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1077db69ef7..8f1da78f1e9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3630,6 +3630,10 @@ "category": "Message", "code": 6358 }, + "Project '{0}' is up to date because it was previously built": { + "category": "Message", + "code": 6359 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index b157a1a4fe4..10ed5280f71 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -514,13 +514,13 @@ namespace ts { // If the upstream project's newest file is older than our oldest output, we // can't be out of date because of it - if (refStatus.newestInputFileTime < oldestOutputFileTime) { + if (refStatus.newestInputFileTime <= oldestOutputFileTime) { continue; } // If the upstream project has only change .d.ts files, and we've built // *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild - if (refStatus.newestDeclarationFileContentChangedTime < oldestOutputFileTime) { + if (refStatus.newestDeclarationFileContentChangedTime <= oldestOutputFileTime) { pseudoUpToDate = true; continue; } @@ -829,7 +829,12 @@ namespace ts { context.verbose(Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, configFileName, status.missingOutputFileName); return; case UpToDateStatusType.UpToDate: - context.verbose(Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFileName, status.newestInputFileTime, status.newestOutputFileTime); + if (status.newestInputFileTime !== undefined) { + context.verbose(Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, configFileName, status.newestInputFileTime, status.newestOutputFileTime); + } + else { + context.verbose(Diagnostics.Project_0_is_up_to_date_because_it_was_previously_built, configFileName); + } return; case UpToDateStatusType.UpToDateWithUpstreamTypes: context.verbose(Diagnostics.Project_0_is_up_to_date_with_its_upstream_types, configFileName); diff --git a/src/harness/unittests/tsbuild.ts b/src/harness/unittests/tsbuild.ts index 809d7bb1b0b..5ac4e19e769 100644 --- a/src/harness/unittests/tsbuild.ts +++ b/src/harness/unittests/tsbuild.ts @@ -1,5 +1,3 @@ -/// - namespace ts { let currentTime = 100; const bfs = new vfs.FileSystem(/*ignoreCase*/ false, { time }); @@ -14,8 +12,8 @@ namespace ts { bfs.makeReadonly(); tick(); - describe("tsbuild tests", () => { - it("can build the sample project 'tests' without error", () => { + describe("tsbuild - sanity check of clean build of 'sample1' project", () => { + it("can build the sample project 'sample1' without error", () => { const fs = bfs.shadow(); const host = new fakes.CompilerHost(fs); const builder = createSolutionBuilder(host, reportDiagnostic, { dry: false, force: false, verbose: false }); @@ -24,13 +22,17 @@ namespace ts { builder.buildProjects(["."]); assertDiagnosticMessages(/*empty*/); }); + }); - it("can detect when and what to rebuild", () => { - const fs = bfs.shadow(); - const host = new fakes.CompilerHost(fs); - const builder = createSolutionBuilder(host, reportDiagnostic, { dry: false, force: false, verbose: true }); + describe("tsbuild - can detect when and what to rebuild", () => { + const fs = bfs.shadow(); + const host = new fakes.CompilerHost(fs); + const builder = createSolutionBuilder(host, reportDiagnostic, { dry: false, force: false, verbose: true }); - fs.chdir("/src/tests"); + fs.chdir("/src/tests"); + + it("Builds the project", () => { + builder.resetBuildContext(); builder.buildProjects(["."]); assertDiagnosticMessages(Diagnostics.Sorted_list_of_input_projects_Colon_0, Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, @@ -40,19 +42,25 @@ namespace ts { Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, Diagnostics.Building_project_0); tick(); + }); - // All three projects are up to date + // All three projects are up to date + it("Detects that all projects are up to date", () => { clearDiagnostics(); + builder.resetBuildContext(); builder.buildProjects(["."]); assertDiagnosticMessages(Diagnostics.Sorted_list_of_input_projects_Colon_0, Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2); tick(); + }); - // Update a file in the leaf node (tests), only it should rebuild the last one + // Update a file in the leaf node (tests), only it should rebuild the last one + it("Only builds the leaf node project", () => { clearDiagnostics(); fs.writeFileSync("/src/tests/index.ts", "const m = 10;"); + builder.resetBuildContext(); builder.buildProjects(["."]); assertDiagnosticMessages(Diagnostics.Sorted_list_of_input_projects_Colon_0, @@ -61,10 +69,13 @@ namespace ts { Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, Diagnostics.Building_project_0); tick(); + }); - // Update a file in the parent (without affecting types), should get fast downstream builds + // Update a file in the parent (without affecting types), should get fast downstream builds + it("Detects type-only changes in upstream projects", () => { clearDiagnostics(); replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET"); + builder.resetBuildContext(); builder.buildProjects(["."]); assertDiagnosticMessages(Diagnostics.Sorted_list_of_input_projects_Colon_0, @@ -81,12 +92,12 @@ namespace ts { if (!fs.statSync(path).isFile()) { throw new Error(`File ${path} does not exist`); } - const old = fs.readFileSync(path, 'utf-8'); + const old = fs.readFileSync(path, "utf-8"); if (old.indexOf(oldText) < 0) { throw new Error(`Text "${oldText}" does not exist in file ${path}`); } const newContent = old.replace(oldText, newText); - fs.writeFileSync(path, newContent, 'utf-8'); + fs.writeFileSync(path, newContent, "utf-8"); } function assertDiagnosticMessages(...expected: DiagnosticMessage[]) { @@ -114,8 +125,9 @@ namespace ts { } function tick() { - currentTime += 100000; + currentTime += 60_000; } + function time() { return currentTime; } @@ -126,7 +138,7 @@ namespace ts { const file = getBaseFileName(path); vfs.writeFileSync(virtualRoot + "/" + file, Harness.IO.readFile(localRoot + "/" + file)); } - for (const dir of Harness.IO.getDirectories(localRoot)){ + for (const dir of Harness.IO.getDirectories(localRoot)) { loadFsMirror(vfs, localRoot + "/" + dir, virtualRoot + "/" + dir); } }