Skip to content

Commit dc21283

Browse files
committed
Save dts change time in buildinfo itself
1 parent 15fe24e commit dc21283

File tree

184 files changed

+5243
-2384
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

184 files changed

+5243
-2384
lines changed

src/compiler/builder.ts

+96-25
Large diffs are not rendered by default.

src/compiler/builderPublic.ts

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ namespace ts {
2525
*/
2626
/*@internal*/
2727
storeFilesChangingSignatureDuringEmit?: boolean;
28+
/**
29+
* Gets the current time
30+
*/
31+
/*@internal*/
32+
now?(): Date;
2833
}
2934

3035
/**

src/compiler/builderState.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace ts {
2929
readonly exportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined;
3030
}
3131

32-
export interface BuilderState {
32+
export interface BuilderState extends ReusableBuilderState {
3333
/**
3434
* Information of the file eg. its version, signature etc
3535
*/
@@ -423,7 +423,7 @@ namespace ts {
423423
const firstDts = firstOrUndefined(emitOutput.outputFiles);
424424
if (firstDts) {
425425
Debug.assert(isDeclarationFileName(firstDts.name), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
426-
latestSignature = (computeHash || generateDjb2Hash)(firstDts.text);
426+
latestSignature = computeSignature(firstDts.text, computeHash);
427427
if (exportedModulesMapCache && latestSignature !== prevSignature) {
428428
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
429429
}
@@ -447,6 +447,10 @@ namespace ts {
447447
return latestSignature !== prevSignature;
448448
}
449449

450+
export function computeSignature(text: string, computeHash: ComputeHash | undefined) {
451+
return (computeHash || generateDjb2Hash)(text);
452+
}
453+
450454
/**
451455
* Coverts the declaration emit result into exported modules map
452456
*/

src/compiler/commandLineParser.ts

+9
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ namespace ts {
522522
name: "outFile",
523523
type: "string",
524524
affectsEmit: true,
525+
affectsDeclarationPath: true,
525526
isFilePath: true,
526527
paramType: Diagnostics.FILE,
527528
showInSimplifiedHelpView: true,
@@ -533,6 +534,7 @@ namespace ts {
533534
name: "outDir",
534535
type: "string",
535536
affectsEmit: true,
537+
affectsDeclarationPath: true,
536538
isFilePath: true,
537539
paramType: Diagnostics.DIRECTORY,
538540
showInSimplifiedHelpView: true,
@@ -543,6 +545,7 @@ namespace ts {
543545
name: "rootDir",
544546
type: "string",
545547
affectsEmit: true,
548+
affectsDeclarationPath: true,
546549
isFilePath: true,
547550
paramType: Diagnostics.LOCATION,
548551
category: Diagnostics.Modules,
@@ -988,6 +991,7 @@ namespace ts {
988991
name: "out",
989992
type: "string",
990993
affectsEmit: true,
994+
affectsDeclarationPath: true,
991995
isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files
992996
// for correct behaviour, please use outFile
993997
category: Diagnostics.Backwards_Compatibility,
@@ -1144,6 +1148,7 @@ namespace ts {
11441148
name: "declarationDir",
11451149
type: "string",
11461150
affectsEmit: true,
1151+
affectsDeclarationPath: true,
11471152
isFilePath: true,
11481153
paramType: Diagnostics.DIRECTORY,
11491154
category: Diagnostics.Emit,
@@ -1281,6 +1286,10 @@ namespace ts {
12811286
export const affectsEmitOptionDeclarations: readonly CommandLineOption[] =
12821287
optionDeclarations.filter(option => !!option.affectsEmit);
12831288

1289+
/* @internal */
1290+
export const affectsDeclarationPathOptionDeclarations: readonly CommandLineOption[] =
1291+
optionDeclarations.filter(option => !!option.affectsDeclarationPath);
1292+
12841293
/* @internal */
12851294
export const moduleResolutionOptionDeclarations: readonly CommandLineOption[] =
12861295
optionDeclarations.filter(option => !!option.affectsModuleResolution);

src/compiler/tsbuildPublic.ts

+72-29
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ namespace ts {
7272
return date1 ? date2 > date1 ? date2 : date1 : date2;
7373
}
7474

75+
/*@internal*/
76+
export function getCurrentTime(host: { now?(): Date; }) {
77+
return host.now ? host.now() : new Date();
78+
}
79+
7580
export type ReportEmitErrorSummary = (errorCount: number, filesInError: (ReportFileInError | undefined)[]) => void;
7681

7782
export interface ReportFileInError {
@@ -985,12 +990,12 @@ namespace ts {
985990
let newestDeclarationFileContentChangedTime: Date | undefined;
986991
const emitterDiagnostics = createDiagnosticCollection();
987992
const emittedOutputs = new Map<Path, string>();
988-
const buildInfoEntry = state.buildInfoCache.get(projectPath);
993+
const isOutFile = outFile(config.options);
989994
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
990995
if (resultFlags === BuildResultFlags.DeclarationOutputUnchanged && isDeclarationFileName(name)) {
991996
// Check for unchanged .d.ts files
992997
if (state.readFileWithCache(name) === text) {
993-
if (config.options.composite) {
998+
if (config.options.composite && isOutFile) {
994999
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, name));
9951000
}
9961001
}
@@ -1001,10 +1006,7 @@ namespace ts {
10011006

10021007
const path = toPath(state, name);
10031008
emittedOutputs.set(path, name);
1004-
if (buildInfoEntry?.path === path) {
1005-
buildInfoEntry.buildInfo = buildInfo!;
1006-
buildInfoEntry.modifiedTime = getCurrentTime(state);
1007-
}
1009+
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
10081010
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
10091011
});
10101012

@@ -1022,12 +1024,7 @@ namespace ts {
10221024
Debug.assertIsDefined(program);
10231025
Debug.assert(step === BuildStep.EmitBuildInfo);
10241026
const emitResult = program.emitBuildInfo((name, text, writeByteOrderMark, onError, sourceFiles, data) => {
1025-
const path = toPath(state, name);
1026-
const buildInfo = state.buildInfoCache.get(projectPath);
1027-
if (buildInfo?.path === path) {
1028-
buildInfo.buildInfo = data!.buildInfo!;
1029-
buildInfo.modifiedTime = getCurrentTime(state);
1030-
}
1027+
if (data?.buildInfo) setBuildInfo(state, data.buildInfo, projectPath, program!.getCompilerOptions());
10311028
if (writeFileCallback) writeFileCallback(name, text, writeByteOrderMark, onError, sourceFiles, data);
10321029
else state.compilerHost.writeFile(name, text, writeByteOrderMark, onError, sourceFiles, data);
10331030
}, cancellationToken);
@@ -1076,7 +1073,7 @@ namespace ts {
10761073
state.diagnostics.delete(projectPath);
10771074
state.projectStatus.set(projectPath, {
10781075
type: UpToDateStatusType.UpToDate,
1079-
newestDeclarationFileContentChangedTime: anyDtsChange ? undefined : newestDeclarationFileContentChangedTime,
1076+
newestDeclarationFileContentChangedTime: newestDeclarationFileContentChangedTime || getDtsChangeTime(state, config.options, projectPath),
10801077
oldestOutputFileName
10811078
});
10821079
afterProgramDone(state, program, config);
@@ -1127,14 +1124,10 @@ namespace ts {
11271124
Debug.assert(!!outputFiles.length);
11281125
const emitterDiagnostics = createDiagnosticCollection();
11291126
const emittedOutputs = new Map<Path, string>();
1130-
const buildInfoEntry = state.buildInfoCache.get(projectPath);
11311127
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
11321128
const path = toPath(state, name);
11331129
emittedOutputs.set(path, name);
1134-
if (buildInfoEntry?.path === path) {
1135-
buildInfoEntry.buildInfo = buildInfo!;
1136-
buildInfoEntry.modifiedTime = getCurrentTime(state);
1137-
}
1130+
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, config.options);
11381131
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
11391132
});
11401133

@@ -1423,6 +1416,18 @@ namespace ts {
14231416
};
14241417
}
14251418

1419+
function setBuildInfo(state: SolutionBuilderState, buildInfo: BuildInfo, resolvedConfigPath: ResolvedConfigFilePath, options: CompilerOptions) {
1420+
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options)!;
1421+
const existing = getBuildInfoCacheEntry(state, buildInfoPath, resolvedConfigPath);
1422+
if (existing) {
1423+
existing.buildInfo = buildInfo;
1424+
existing.modifiedTime = getCurrentTime(state.host);
1425+
}
1426+
else {
1427+
state.buildInfoCache.set(resolvedConfigPath, { path: toPath(state, buildInfoPath), buildInfo, modifiedTime: getCurrentTime(state.host) });
1428+
}
1429+
}
1430+
14261431
function getBuildInfoCacheEntry(state: SolutionBuilderState, buildInfoPath: string, resolvedConfigPath: ResolvedConfigFilePath) {
14271432
const path = toPath(state, buildInfoPath);
14281433
const existing = state.buildInfoCache.get(resolvedConfigPath);
@@ -1470,7 +1475,8 @@ namespace ts {
14701475
for (const ref of project.projectReferences) {
14711476
const resolvedRef = resolveProjectReferencePath(ref);
14721477
const resolvedRefPath = toResolvedConfigFilePath(state, resolvedRef);
1473-
const refStatus = getUpToDateStatus(state, parseConfigFile(state, resolvedRef, resolvedRefPath), resolvedRefPath);
1478+
const resolvedConfig = parseConfigFile(state, resolvedRef, resolvedRefPath)!;
1479+
const refStatus = getUpToDateStatus(state, resolvedConfig, resolvedRefPath);
14741480

14751481
// Its a circular reference ignore the status of this project
14761482
if (refStatus.type === UpToDateStatusType.ComputingUpstream ||
@@ -1496,7 +1502,7 @@ namespace ts {
14961502
};
14971503
}
14981504

1499-
if (!force) (referenceStatuses ||= []).push({ ref, refStatus });
1505+
if (!force) (referenceStatuses ||= []).push({ ref, refStatus, resolvedRefPath, resolvedConfig });
15001506
}
15011507
}
15021508
if (force) return { type: UpToDateStatusType.ForceBuild };
@@ -1507,6 +1513,7 @@ namespace ts {
15071513
let oldestOutputFileName = "(none)";
15081514
let oldestOutputFileTime = maximumDate;
15091515
let buildInfoTime: Date | undefined;
1516+
let newestDeclarationFileContentChangedTime;
15101517
if (buildInfoPath) {
15111518
const buildInfoCacheEntry = getBuildInfoCacheEntry(state, buildInfoPath, resolvedPath);
15121519
buildInfoTime = buildInfoCacheEntry?.modifiedTime || ts.getModifiedTime(host, buildInfoPath);
@@ -1540,6 +1547,7 @@ namespace ts {
15401547

15411548
oldestOutputFileTime = buildInfoTime;
15421549
oldestOutputFileName = buildInfoPath;
1550+
newestDeclarationFileContentChangedTime = buildInfo.program?.dtsChangeTime ? new Date(buildInfo.program.dtsChangeTime) : undefined;
15431551
}
15441552

15451553
// Check input files
@@ -1601,18 +1609,31 @@ namespace ts {
16011609
}
16021610
}
16031611

1612+
const seenRefs = buildInfoPath ? new Set<ResolvedConfigFilePath>() : undefined;
1613+
const buildInfoCacheEntry = state.buildInfoCache.get(resolvedPath)!;
1614+
seenRefs?.add(resolvedPath);
1615+
16041616
let pseudoUpToDate = false;
16051617
let usesPrepend = false;
16061618
let upstreamChangedProject: string | undefined;
16071619
if (referenceStatuses) {
1608-
for (const { ref, refStatus } of referenceStatuses) {
1620+
for (const { ref, refStatus, resolvedConfig, resolvedRefPath } of referenceStatuses) {
16091621
usesPrepend = usesPrepend || !!(ref.prepend);
16101622
// If the upstream project's newest file is older than our oldest output, we
16111623
// can't be out of date because of it
16121624
if (refStatus.newestInputFileTime && refStatus.newestInputFileTime <= oldestOutputFileTime) {
16131625
continue;
16141626
}
16151627

1628+
// Check if tsbuildinfo path is shared, then we need to rebuild
1629+
if (buildInfoCacheEntry && hasSameBuildInfo(state, buildInfoCacheEntry, seenRefs!, resolvedConfig, resolvedRefPath)) {
1630+
return {
1631+
type: UpToDateStatusType.OutOfDateWithUpstream,
1632+
outOfDateOutputFileName: buildInfoPath!,
1633+
newerProjectName: ref.path
1634+
};
1635+
}
1636+
16161637
// If the upstream project has only change .d.ts files, and we've built
16171638
// *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild
16181639
if (refStatus.newestDeclarationFileContentChangedTime && refStatus.newestDeclarationFileContentChangedTime <= oldestOutputFileTime) {
@@ -1657,13 +1678,31 @@ namespace ts {
16571678
// Up to date
16581679
return {
16591680
type: pseudoUpToDate ? UpToDateStatusType.UpToDateWithUpstreamTypes : UpToDateStatusType.UpToDate,
1660-
newestDeclarationFileContentChangedTime: undefined,
1681+
newestDeclarationFileContentChangedTime,
16611682
newestInputFileTime,
16621683
newestInputFileName,
16631684
oldestOutputFileName
16641685
};
16651686
}
16661687

1688+
function hasSameBuildInfo(state: SolutionBuilderState, buildInfoCacheEntry: BuildInfoCacheEntry, seenRefs: Set<ResolvedConfigFilePath>, resolvedConfig: ParsedCommandLine, resolvedRefPath: ResolvedConfigFilePath) {
1689+
if (seenRefs.has(resolvedRefPath)) return false;
1690+
seenRefs.add(resolvedRefPath);
1691+
const refBuildInfo = state.buildInfoCache.get(resolvedRefPath)!;
1692+
if (refBuildInfo.path === buildInfoCacheEntry.path) return true;
1693+
1694+
if (resolvedConfig.projectReferences) {
1695+
// Check references
1696+
for (const ref of resolvedConfig.projectReferences) {
1697+
const resolvedRef = resolveProjectReferencePath(ref);
1698+
const resolvedRefPath = toResolvedConfigFilePath(state, resolvedRef);
1699+
const resolvedConfig = parseConfigFile(state, resolvedRef, resolvedRefPath)!;
1700+
if (hasSameBuildInfo(state, buildInfoCacheEntry, seenRefs, resolvedConfig, resolvedRefPath)) return true;
1701+
}
1702+
}
1703+
return false;
1704+
}
1705+
16671706
function getUpToDateStatus(state: SolutionBuilderState, project: ParsedCommandLine | undefined, resolvedPath: ResolvedConfigFilePath): UpToDateStatus {
16681707
if (project === undefined) {
16691708
return { type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" };
@@ -1679,16 +1718,13 @@ namespace ts {
16791718
return actual;
16801719
}
16811720

1682-
function getCurrentTime(state: SolutionBuilderState) {
1683-
return state.host.now ? state.host.now() : new Date();
1684-
}
1685-
16861721
function updateOutputTimestampsWorker(state: SolutionBuilderState, proj: ParsedCommandLine, anyDtsChange: boolean, verboseMessage: DiagnosticMessage, newestDeclarationFileContentChangedTime?: Date, skipOutputs?: ESMap<Path, string>) {
16871722
if (proj.options.noEmit) return undefined;
16881723

16891724
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(proj.options);
16901725
const { host } = state;
16911726
const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames());
1727+
const isOutFile = outFile(proj.options);
16921728
if (!skipOutputs || outputs.length !== skipOutputs.size) {
16931729
let reportVerbose = !!state.options.verbose;
16941730
let now: Date | undefined;
@@ -1697,7 +1733,7 @@ namespace ts {
16971733
continue;
16981734
}
16991735

1700-
if (proj.options.composite && !anyDtsChange && isDeclarationFileName(file)) {
1736+
if (proj.options.composite && isOutFile && !anyDtsChange && isDeclarationFileName(file)) {
17011737
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, file));
17021738
}
17031739

@@ -1707,22 +1743,29 @@ namespace ts {
17071743
reportStatus(state, verboseMessage, proj.options.configFilePath!);
17081744
}
17091745

1710-
host.setModifiedTime(file, now ||= getCurrentTime(state));
1746+
host.setModifiedTime(file, now ||= getCurrentTime(state.host));
17111747
}
17121748
}
17131749
}
17141750

17151751
return newestDeclarationFileContentChangedTime;
17161752
}
17171753

1754+
function getDtsChangeTime(state: SolutionBuilderState, options: CompilerOptions, resolvedConfigPath: ResolvedConfigFilePath) {
1755+
if (!options.composite || outFile(options)) return undefined;
1756+
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options)!;
1757+
const buildInfo = getBuildInfo(state, buildInfoPath, resolvedConfigPath, /*modifiedTime*/ undefined);
1758+
return buildInfo?.program?.dtsChangeTime ? new Date(buildInfo.program.dtsChangeTime) : undefined;
1759+
}
1760+
17181761
function updateOutputTimestamps(state: SolutionBuilderState, proj: ParsedCommandLine, resolvedPath: ResolvedConfigFilePath) {
17191762
if (state.options.dry) {
17201763
return reportStatus(state, Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!);
17211764
}
17221765
const priorNewestUpdateTime = updateOutputTimestampsWorker(state, proj, /*anyDtsChange*/ false, Diagnostics.Updating_output_timestamps_of_project_0);
17231766
state.projectStatus.set(resolvedPath, {
17241767
type: UpToDateStatusType.UpToDate,
1725-
newestDeclarationFileContentChangedTime: priorNewestUpdateTime,
1768+
newestDeclarationFileContentChangedTime: priorNewestUpdateTime || getDtsChangeTime(state, proj.options, resolvedPath),
17261769
oldestOutputFileName: getFirstProjectOutput(proj, !state.host.useCaseSensitiveFileNames())
17271770
});
17281771
}

src/compiler/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6421,6 +6421,7 @@ namespace ts {
64216421
affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics
64226422
affectsEmit?: true; // true if the options affects emit
64236423
affectsProgramStructure?: true; // true if program should be reconstructed from root files if option changes and does not affect module resolution as affectsModuleResolution indirectly means program needs to reconstructed
6424+
affectsDeclarationPath?: true; // true if the options affects declaration file path computed
64246425
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
64256426
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
64266427
}
@@ -6790,6 +6791,7 @@ namespace ts {
67906791
// For testing:
67916792
/*@internal*/ disableUseFileVersionAsSignature?: boolean;
67926793
/*@internal*/ storeFilesChangingSignatureDuringEmit?: boolean;
6794+
/*@internal*/ now?(): Date;
67936795
}
67946796

67956797
/** true if --out otherwise source file name */

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -6465,6 +6465,10 @@ namespace ts {
64656465
return optionsHaveChanges(oldOptions, newOptions, affectsEmitOptionDeclarations);
64666466
}
64676467

6468+
export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean {
6469+
return optionsHaveChanges(oldOptions, newOptions, affectsDeclarationPathOptionDeclarations);
6470+
}
6471+
64686472
export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown {
64696473
return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : options[option.name];
64706474
}

0 commit comments

Comments
 (0)