Skip to content

Commit 4fb6773

Browse files
committed
Store output time stamps for non incremental builds
1 parent 128008a commit 4fb6773

19 files changed

+300
-321
lines changed

src/compiler/tsbuildPublic.ts

+40-11
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ namespace ts {
245245
readonly projectStatus: ESMap<ResolvedConfigFilePath, UpToDateStatus>;
246246
readonly extendedConfigCache: ESMap<string, ExtendedConfigCacheEntry>;
247247
readonly buildInfoCache: ESMap<ResolvedConfigFilePath, BuildInfoCacheEntry>;
248+
readonly outputTimeStamps: ESMap<ResolvedConfigFilePath, ESMap<Path, Date>>;
248249

249250
readonly builderPrograms: ESMap<ResolvedConfigFilePath, T>;
250251
readonly diagnostics: ESMap<ResolvedConfigFilePath, readonly Diagnostic[]>;
@@ -330,6 +331,7 @@ namespace ts {
330331
projectStatus: new Map(),
331332
extendedConfigCache: new Map(),
332333
buildInfoCache: new Map(),
334+
outputTimeStamps: new Map(),
333335

334336
builderPrograms: new Map(),
335337
diagnostics: new Map(),
@@ -493,6 +495,7 @@ namespace ts {
493495
mutateMapSkippingNewValues(state.projectPendingBuild, currentProjects, noopOnDelete);
494496
mutateMapSkippingNewValues(state.projectErrorsReported, currentProjects, noopOnDelete);
495497
mutateMapSkippingNewValues(state.buildInfoCache, currentProjects, noopOnDelete);
498+
mutateMapSkippingNewValues(state.outputTimeStamps, currentProjects, noopOnDelete);
496499

497500
// Remove watches for the program no longer in the solution
498501
if (state.watch) {
@@ -983,15 +986,23 @@ namespace ts {
983986
const existingBuildInfo = state.buildInfoCache.get(projectPath)?.buildInfo || undefined;
984987
const emitterDiagnostics = createDiagnosticCollection();
985988
const emittedOutputs = new Map<Path, string>();
989+
const options = program.getCompilerOptions();
990+
const isIncremental = isIncrementalCompilation(options);
991+
let outputTimeStampMap: ESMap<Path, Date> | undefined;
992+
let now: Date | undefined;
986993
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
994+
const path = toPath(state, name);
987995
emittedOutputs.set(toPath(state, name), name);
988996
if (buildInfo) {
989-
setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
997+
setBuildInfo(state, buildInfo, projectPath, options);
990998
if (buildInfo.program?.dtsChangeTime !== existingBuildInfo?.program?.dtsChangeTime) {
991999
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
9921000
}
9931001
}
9941002
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
1003+
if (!isIncremental) {
1004+
(outputTimeStampMap ||= getOutputTimeStampMap(state, projectPath)).set(path, now ||= getCurrentTime(state.host));
1005+
}
9951006
});
9961007

9971008
finishEmit(
@@ -1050,7 +1061,7 @@ namespace ts {
10501061
}
10511062

10521063
// Update time stamps for rest of the outputs
1053-
updateOutputTimestampsWorker(state, config, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
1064+
updateOutputTimestampsWorker(state, config, projectPath, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
10541065
state.diagnostics.delete(projectPath);
10551066
state.projectStatus.set(projectPath, {
10561067
type: UpToDateStatusType.UpToDate,
@@ -1402,6 +1413,12 @@ namespace ts {
14021413
};
14031414
}
14041415

1416+
function getOutputTimeStampMap(state: SolutionBuilderState, resolvedConfigFilePath: ResolvedConfigFilePath) {
1417+
let result = state.outputTimeStamps.get(resolvedConfigFilePath);
1418+
if (!result) state.outputTimeStamps.set(resolvedConfigFilePath, result = new Map());
1419+
return result;
1420+
}
1421+
14051422
function setBuildInfo(state: SolutionBuilderState, buildInfo: BuildInfo, resolvedConfigPath: ResolvedConfigFilePath, options: CompilerOptions) {
14061423
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options)!;
14071424
const existing = getBuildInfoCacheEntry(state, buildInfoPath, resolvedConfigPath);
@@ -1573,9 +1590,12 @@ namespace ts {
15731590
if (!buildInfoPath) {
15741591
// Collect the expected outputs of this project
15751592
const outputs = getAllProjectOutputs(project, !host.useCaseSensitiveFileNames());
1593+
const outputTimeStampMap = getOutputTimeStampMap(state, resolvedPath);
15761594
for (const output of outputs) {
1595+
const path = toPath(state, output);
15771596
// Output is missing; can stop checking
1578-
const outputTime = ts.getModifiedTime(state.host, output);
1597+
let outputTime = outputTimeStampMap.get(path);
1598+
if (!outputTime) outputTimeStampMap.set(path, outputTime = ts.getModifiedTime(state.host, output));
15791599
if (outputTime === missingFileModifiedTime) {
15801600
return {
15811601
type: UpToDateStatusType.OutputMissing,
@@ -1711,37 +1731,46 @@ namespace ts {
17111731
function updateOutputTimestampsWorker(
17121732
state: SolutionBuilderState,
17131733
proj: ParsedCommandLine,
1734+
projectPath: ResolvedConfigFilePath,
17141735
verboseMessage: DiagnosticMessage,
17151736
skipOutputs?: ESMap<Path, string>
17161737
) {
17171738
if (proj.options.noEmit) return;
1739+
let now: Date | undefined;
17181740
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(proj.options);
17191741
if (buildInfoPath) {
17201742
if (!skipOutputs?.has(toPath(state, buildInfoPath))) {
17211743
if (!!state.options.verbose) reportStatus(state, verboseMessage, proj.options.configFilePath!);
1722-
state.host.setModifiedTime(buildInfoPath, getCurrentTime(state.host));
1744+
state.host.setModifiedTime(buildInfoPath, now = getCurrentTime(state.host));
1745+
getBuildInfoCacheEntry(state, buildInfoPath, projectPath)!.modifiedTime = now;
17231746
}
1747+
state.outputTimeStamps.delete(projectPath);
17241748
return;
17251749
}
17261750

17271751
const { host } = state;
17281752
const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames());
1753+
const outputTimeStampMap = getOutputTimeStampMap(state, projectPath);
1754+
const modifiedOutputs = new Set<Path>();
17291755
if (!skipOutputs || outputs.length !== skipOutputs.size) {
17301756
let reportVerbose = !!state.options.verbose;
1731-
let now: Date | undefined;
17321757
for (const file of outputs) {
1733-
if (skipOutputs && skipOutputs.has(toPath(state, file))) {
1734-
continue;
1735-
}
1736-
1758+
const path = toPath(state, file);
1759+
if (skipOutputs?.has(path)) continue;
17371760
if (reportVerbose) {
17381761
reportVerbose = false;
17391762
reportStatus(state, verboseMessage, proj.options.configFilePath!);
17401763
}
1741-
17421764
host.setModifiedTime(file, now ||= getCurrentTime(state.host));
1765+
outputTimeStampMap.set(path, now);
1766+
modifiedOutputs.add(path);
17431767
}
17441768
}
1769+
1770+
// Clear out timestamps not in output list any more
1771+
outputTimeStampMap.forEach((_value, key) => {
1772+
if (!skipOutputs?.has(key) && !modifiedOutputs.has(key)) outputTimeStampMap.delete(key);
1773+
});
17451774
}
17461775

17471776
function getDtsChangeTime(state: SolutionBuilderState, options: CompilerOptions, resolvedConfigPath: ResolvedConfigFilePath) {
@@ -1755,7 +1784,7 @@ namespace ts {
17551784
if (state.options.dry) {
17561785
return reportStatus(state, Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!);
17571786
}
1758-
updateOutputTimestampsWorker(state, proj, Diagnostics.Updating_output_timestamps_of_project_0);
1787+
updateOutputTimestampsWorker(state, proj, resolvedPath, Diagnostics.Updating_output_timestamps_of_project_0);
17591788
state.projectStatus.set(resolvedPath, {
17601789
type: UpToDateStatusType.UpToDate,
17611790
newestDeclarationFileContentChangedTime: getDtsChangeTime(state, proj.options, resolvedPath),

tests/baselines/reference/tsbuild/outFile/rebuilds-completely-when-command-line-incremental-flag-changes-between-non-dts-changes.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -821,24 +821,24 @@ console.log(s);console.log(s);
821821

822822
Output::
823823
/lib/tsc --b /src/third --verbose --incremental
824-
[[90m12:01:04 AM[0m] Projects in this build:
824+
[[90m12:01:05 AM[0m] Projects in this build:
825825
* src/first/tsconfig.json
826826
* src/second/tsconfig.json
827827
* src/third/tsconfig.json
828828

829-
[[90m12:01:05 AM[0m] Project 'src/first/tsconfig.json' is out of date because oldest output 'src/first/bin/first-output.tsbuildinfo' is older than newest input 'src/first/first_PART1.ts'
829+
[[90m12:01:06 AM[0m] Project 'src/first/tsconfig.json' is out of date because oldest output 'src/first/bin/first-output.tsbuildinfo' is older than newest input 'src/first/first_PART1.ts'
830830

831-
[[90m12:01:06 AM[0m] Building project '/src/first/tsconfig.json'...
831+
[[90m12:01:07 AM[0m] Building project '/src/first/tsconfig.json'...
832832

833-
[[90m12:01:15 AM[0m] Project 'src/second/tsconfig.json' is up to date because newest input 'src/second/second_part1.ts' is older than oldest output 'src/2/second-output.tsbuildinfo'
833+
[[90m12:01:16 AM[0m] Project 'src/second/tsconfig.json' is up to date because newest input 'src/second/second_part1.ts' is older than oldest output 'src/2/second-output.tsbuildinfo'
834834

835-
[[90m12:01:16 AM[0m] Project 'src/third/tsconfig.json' is out of date because output of its dependency 'src/first' has changed
835+
[[90m12:01:17 AM[0m] Project 'src/third/tsconfig.json' is out of date because output of its dependency 'src/first' has changed
836836

837-
[[90m12:01:17 AM[0m] Updating output of project '/src/third/tsconfig.json'...
837+
[[90m12:01:18 AM[0m] Updating output of project '/src/third/tsconfig.json'...
838838

839-
[[90m12:01:18 AM[0m] Cannot update output of project '/src/third/tsconfig.json' because there was error reading file 'src/third/thirdjs/output/third-output.js'
839+
[[90m12:01:19 AM[0m] Cannot update output of project '/src/third/tsconfig.json' because there was error reading file 'src/third/thirdjs/output/third-output.js'
840840

841-
[[90m12:01:19 AM[0m] Building project '/src/third/tsconfig.json'...
841+
[[90m12:01:20 AM[0m] Building project '/src/third/tsconfig.json'...
842842

843843
exitCode:: ExitStatus.Success
844844

tests/baselines/reference/tsbuild/outfile-concat/when-final-project-is-not-composite-but-uses-project-references.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -1774,20 +1774,20 @@ console.log(s);
17741774

17751775
Output::
17761776
/lib/tsc --b /src/third --verbose
1777-
[[90m12:00:47 AM[0m] Projects in this build:
1777+
[[90m12:00:48 AM[0m] Projects in this build:
17781778
* src/first/tsconfig.json
17791779
* src/second/tsconfig.json
17801780
* src/third/tsconfig.json
17811781

1782-
[[90m12:00:48 AM[0m] Project 'src/first/tsconfig.json' is out of date because oldest output 'src/first/bin/first-output.tsbuildinfo' is older than newest input 'src/first/first_PART1.ts'
1782+
[[90m12:00:49 AM[0m] Project 'src/first/tsconfig.json' is out of date because oldest output 'src/first/bin/first-output.tsbuildinfo' is older than newest input 'src/first/first_PART1.ts'
17831783

1784-
[[90m12:00:49 AM[0m] Building project '/src/first/tsconfig.json'...
1784+
[[90m12:00:50 AM[0m] Building project '/src/first/tsconfig.json'...
17851785

1786-
[[90m12:00:58 AM[0m] Project 'src/second/tsconfig.json' is up to date because newest input 'src/second/second_part1.ts' is older than oldest output 'src/2/second-output.tsbuildinfo'
1786+
[[90m12:00:59 AM[0m] Project 'src/second/tsconfig.json' is up to date because newest input 'src/second/second_part1.ts' is older than oldest output 'src/2/second-output.tsbuildinfo'
17871787

1788-
[[90m12:00:59 AM[0m] Project 'src/third/tsconfig.json' is out of date because output of its dependency 'src/first' has changed
1788+
[[90m12:01:00 AM[0m] Project 'src/third/tsconfig.json' is out of date because output of its dependency 'src/first' has changed
17891789

1790-
[[90m12:01:00 AM[0m] Building project '/src/third/tsconfig.json'...
1790+
[[90m12:01:01 AM[0m] Building project '/src/third/tsconfig.json'...
17911791

17921792
exitCode:: ExitStatus.Success
17931793

tests/baselines/reference/tsbuild/outputPaths/when-rootDir-is-not-specified.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ Input::
6363

6464
Output::
6565
/lib/tsc --b /src/tsconfig.json -v
66-
[[90m12:00:14 AM[0m] Projects in this build:
66+
[[90m12:00:15 AM[0m] Projects in this build:
6767
* src/tsconfig.json
6868

69-
[[90m12:00:15 AM[0m] Project 'src/tsconfig.json' is up to date because newest input 'src/src/index.ts' is older than oldest output 'src/dist/index.js'
69+
[[90m12:00:16 AM[0m] Project 'src/tsconfig.json' is up to date because newest input 'src/src/index.ts' is older than oldest output 'src/dist/index.js'
7070

7171
exitCode:: ExitStatus.Success
7272

tests/baselines/reference/tsbuild/outputPaths/when-rootDir-is-specified.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ Input::
6363

6464
Output::
6565
/lib/tsc --b /src/tsconfig.json -v
66-
[[90m12:00:14 AM[0m] Projects in this build:
66+
[[90m12:00:15 AM[0m] Projects in this build:
6767
* src/tsconfig.json
6868

69-
[[90m12:00:15 AM[0m] Project 'src/tsconfig.json' is up to date because newest input 'src/src/index.ts' is older than oldest output 'src/dist/index.js'
69+
[[90m12:00:16 AM[0m] Project 'src/tsconfig.json' is up to date because newest input 'src/src/index.ts' is older than oldest output 'src/dist/index.js'
7070

7171
exitCode:: ExitStatus.Success
7272

tests/baselines/reference/tsbuildWatch/moduleResolution/build-mode-watches-for-changes-to-package-json-main-fields.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.ts' does not e
110110
File '/user/username/projects/myproject/packages/pkg2/build/const.tsx' does not exist.
111111
File '/user/username/projects/myproject/packages/pkg2/build/const.d.ts' exist - use it as a name resolution result.
112112
======== Module name './const.js' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/const.d.ts'. ========
113-
[[90m12:01:15 AM[0m] Found 0 errors. Watching for file changes.
113+
[[90m12:01:16 AM[0m] Found 0 errors. Watching for file changes.
114114

115115

116116

@@ -349,11 +349,11 @@ setModifiedTimes:: {}
349349

350350
Output::
351351
>> Screen clear
352-
[[90m12:01:19 AM[0m] File change detected. Starting incremental compilation...
352+
[[90m12:01:20 AM[0m] File change detected. Starting incremental compilation...
353353

354-
[[90m12:01:20 AM[0m] Project 'packages/pkg1/tsconfig.json' is out of date because oldest output 'packages/pkg1/build/index.js' is older than newest input 'packages/pkg2/package.json'
354+
[[90m12:01:21 AM[0m] Project 'packages/pkg1/tsconfig.json' is out of date because oldest output 'packages/pkg1/build/index.js' is older than newest input 'packages/pkg2/package.json'
355355

356-
[[90m12:01:21 AM[0m] Building project '/user/username/projects/myproject/packages/pkg1/tsconfig.json'...
356+
[[90m12:01:22 AM[0m] Building project '/user/username/projects/myproject/packages/pkg1/tsconfig.json'...
357357

358358
======== Resolving module 'pkg2' from '/user/username/projects/myproject/packages/pkg1/index.ts'. ========
359359
Module resolution kind is not specified, using 'NodeJs'.
@@ -385,7 +385,7 @@ Resolving real path for '/user/username/projects/myproject/node_modules/pkg2/bui
385385
1 import type { TheNum } from 'pkg2'
386386
   ~~~~~~
387387

388-
[[90m12:01:22 AM[0m] Found 1 error. Watching for file changes.
388+
[[90m12:01:23 AM[0m] Found 1 error. Watching for file changes.
389389

390390

391391

@@ -463,7 +463,6 @@ directoryExists:: {
463463
}
464464

465465
getModifiedTimes:: {
466-
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1,
467466
"/user/username/projects/myproject/packages/pkg1/tsconfig.json": 1
468467
}
469468

@@ -486,11 +485,11 @@ setModifiedTimes:: {}
486485

487486
Output::
488487
>> Screen clear
489-
[[90m12:01:26 AM[0m] File change detected. Starting incremental compilation...
488+
[[90m12:01:27 AM[0m] File change detected. Starting incremental compilation...
490489

491-
[[90m12:01:27 AM[0m] Project 'packages/pkg1/tsconfig.json' is out of date because oldest output 'packages/pkg1/build/index.js' is older than newest input 'packages/pkg2/package.json'
490+
[[90m12:01:28 AM[0m] Project 'packages/pkg1/tsconfig.json' is out of date because oldest output 'packages/pkg1/build/index.js' is older than newest input 'packages/pkg2/package.json'
492491

493-
[[90m12:01:28 AM[0m] Building project '/user/username/projects/myproject/packages/pkg1/tsconfig.json'...
492+
[[90m12:01:29 AM[0m] Building project '/user/username/projects/myproject/packages/pkg1/tsconfig.json'...
494493

495494
======== Resolving module 'pkg2' from '/user/username/projects/myproject/packages/pkg1/index.ts'. ========
496495
Module resolution kind is not specified, using 'NodeJs'.
@@ -529,7 +528,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.ts' does not e
529528
File '/user/username/projects/myproject/packages/pkg2/build/const.tsx' does not exist.
530529
File '/user/username/projects/myproject/packages/pkg2/build/const.d.ts' exist - use it as a name resolution result.
531530
======== Module name './const.js' was successfully resolved to '/user/username/projects/myproject/packages/pkg2/build/const.d.ts'. ========
532-
[[90m12:01:32 AM[0m] Found 0 errors. Watching for file changes.
531+
[[90m12:01:34 AM[0m] Found 0 errors. Watching for file changes.
533532

534533

535534

@@ -617,8 +616,6 @@ directoryExists:: {
617616
"/node_modules/@types": 1
618617
}
619618

620-
getModifiedTimes:: {
621-
"/user/username/projects/myproject/packages/pkg1/build/index.js": 1
622-
}
619+
getModifiedTimes:: {}
623620

624621
setModifiedTimes:: {}

0 commit comments

Comments
 (0)