Skip to content

Commit 5bccee8

Browse files
committed
Store dts time for --out in the buildInfo
1 parent dc21283 commit 5bccee8

File tree

56 files changed

+3639
-1445
lines changed

Some content is hidden

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

56 files changed

+3639
-1445
lines changed

src/compiler/builder.ts

+148-122
Large diffs are not rendered by default.

src/compiler/commandLineParser.ts

+4
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ namespace ts {
523523
type: "string",
524524
affectsEmit: true,
525525
affectsDeclarationPath: true,
526+
affectsOutFileBuildInfo: true,
526527
isFilePath: true,
527528
paramType: Diagnostics.FILE,
528529
showInSimplifiedHelpView: true,
@@ -556,6 +557,7 @@ namespace ts {
556557
name: "composite",
557558
type: "boolean",
558559
affectsEmit: true,
560+
affectsOutFileBuildInfo: true,
559561
isTSConfigOnly: true,
560562
category: Diagnostics.Projects,
561563
transpileOptionValue: undefined,
@@ -566,6 +568,7 @@ namespace ts {
566568
name: "tsBuildInfoFile",
567569
type: "string",
568570
affectsEmit: true,
571+
affectsOutFileBuildInfo: true,
569572
isFilePath: true,
570573
paramType: Diagnostics.FILE,
571574
category: Diagnostics.Projects,
@@ -992,6 +995,7 @@ namespace ts {
992995
type: "string",
993996
affectsEmit: true,
994997
affectsDeclarationPath: true,
998+
affectsOutFileBuildInfo: true,
995999
isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files
9961000
// for correct behaviour, please use outFile
9971001
category: Diagnostics.Backwards_Compatibility,

src/compiler/emitter.ts

+11
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ namespace ts {
710710
getCanonicalFileName(fileName: string): string;
711711
useCaseSensitiveFileNames(): boolean;
712712
getNewLine(): string;
713+
createHash?(data: string): string;
714+
now?(): Date;
713715
}
714716

715717
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
@@ -780,6 +782,8 @@ namespace ts {
780782
const outputFiles: OutputFile[] = [];
781783
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
782784
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
785+
let changedDtsText: string | undefined;
786+
let changedDtsData: WriteFileCallbackData | undefined;
783787
const emitHost: EmitHost = {
784788
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
785789
getCanonicalFileName: host.getCanonicalFileName,
@@ -806,6 +810,11 @@ namespace ts {
806810
case buildInfoPath:
807811
const newBuildInfo = data!.buildInfo!;
808812
newBuildInfo.program = buildInfo.program;
813+
if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
814+
// Update the output signature
815+
newBuildInfo.program.outSignature = computeSignature(changedDtsText, changedDtsData, maybeBind(host, host.createHash));
816+
newBuildInfo.program.dtsChangeTime = getCurrentTime(host).getTime();
817+
}
809818
// Update sourceFileInfo
810819
const { js, dts, sourceFiles } = buildInfo.bundle!;
811820
newBuildInfo.bundle!.js!.sources = js!.sources;
@@ -817,6 +826,8 @@ namespace ts {
817826
return;
818827
case declarationFilePath:
819828
if (declarationText === text) return;
829+
changedDtsText = text;
830+
changedDtsData = data;
820831
break;
821832
case declarationMapPath:
822833
if (declarationMapText === text) return;

src/compiler/tsbuildPublic.ts

+39-47
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ namespace ts {
6868
return getOrCreateValueFromConfigFileMap(configFileMap, resolved, () => new Map());
6969
}
7070

71-
function newer(date1: Date | undefined, date2: Date): Date | undefined {
72-
return date1 ? date2 > date1 ? date2 : date1 : date2;
73-
}
74-
7571
/*@internal*/
7672
export function getCurrentTime(host: { now?(): Date; }) {
7773
return host.now ? host.now() : new Date();
@@ -987,33 +983,23 @@ namespace ts {
987983
// Actual Emit
988984
const { host, compilerHost } = state;
989985
let resultFlags = BuildResultFlags.DeclarationOutputUnchanged;
990-
let newestDeclarationFileContentChangedTime: Date | undefined;
986+
const existingBuildInfo = state.buildInfoCache.get(projectPath)?.buildInfo || undefined;
991987
const emitterDiagnostics = createDiagnosticCollection();
992988
const emittedOutputs = new Map<Path, string>();
993-
const isOutFile = outFile(config.options);
994989
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
995-
if (resultFlags === BuildResultFlags.DeclarationOutputUnchanged && isDeclarationFileName(name)) {
996-
// Check for unchanged .d.ts files
997-
if (state.readFileWithCache(name) === text) {
998-
if (config.options.composite && isOutFile) {
999-
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, name));
1000-
}
1001-
}
1002-
else {
990+
emittedOutputs.set(toPath(state, name), name);
991+
if (buildInfo) {
992+
setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
993+
if (buildInfo.program?.dtsChangeTime !== existingBuildInfo?.program?.dtsChangeTime) {
1003994
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
1004995
}
1005996
}
1006-
1007-
const path = toPath(state, name);
1008-
emittedOutputs.set(path, name);
1009-
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
1010997
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
1011998
});
1012999

10131000
finishEmit(
10141001
emitterDiagnostics,
10151002
emittedOutputs,
1016-
newestDeclarationFileContentChangedTime,
10171003
outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(config, !host.useCaseSensitiveFileNames()),
10181004
resultFlags
10191005
);
@@ -1045,7 +1031,6 @@ namespace ts {
10451031
function finishEmit(
10461032
emitterDiagnostics: DiagnosticCollection,
10471033
emittedOutputs: ESMap<Path, string>,
1048-
newestDeclarationFileContentChangedTime: Date | undefined,
10491034
oldestOutputFileName: string,
10501035
resultFlags: BuildResultFlags
10511036
) {
@@ -1068,12 +1053,11 @@ namespace ts {
10681053
}
10691054

10701055
// Update time stamps for rest of the outputs
1071-
const anyDtsChange = !(resultFlags & BuildResultFlags.DeclarationOutputUnchanged);
1072-
newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(state, config, anyDtsChange, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, newestDeclarationFileContentChangedTime, emittedOutputs);
1056+
updateOutputTimestampsWorker(state, config, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
10731057
state.diagnostics.delete(projectPath);
10741058
state.projectStatus.set(projectPath, {
10751059
type: UpToDateStatusType.UpToDate,
1076-
newestDeclarationFileContentChangedTime: newestDeclarationFileContentChangedTime || getDtsChangeTime(state, config.options, projectPath),
1060+
newestDeclarationFileContentChangedTime: getDtsChangeTime(state, config.options, projectPath),
10771061
oldestOutputFileName
10781062
});
10791063
afterProgramDone(state, program, config);
@@ -1124,19 +1108,24 @@ namespace ts {
11241108
Debug.assert(!!outputFiles.length);
11251109
const emitterDiagnostics = createDiagnosticCollection();
11261110
const emittedOutputs = new Map<Path, string>();
1111+
let resultFlags = BuildResultFlags.DeclarationOutputUnchanged;
1112+
const existingBuildInfo = state.buildInfoCache.get(projectPath)!.buildInfo as BuildInfo;
11271113
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
1128-
const path = toPath(state, name);
1129-
emittedOutputs.set(path, name);
1130-
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, config.options);
1114+
emittedOutputs.set(toPath(state, name), name);
1115+
if (buildInfo) {
1116+
setBuildInfo(state, buildInfo, projectPath, config.options);
1117+
if (buildInfo.program?.dtsChangeTime !== existingBuildInfo.program?.dtsChangeTime) {
1118+
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
1119+
}
1120+
}
11311121
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
11321122
});
11331123

11341124
const emitDiagnostics = finishEmit(
11351125
emitterDiagnostics,
11361126
emittedOutputs,
1137-
/*newestDeclarationFileContentChangedTime*/ undefined,
11381127
outputFiles[0].name,
1139-
BuildResultFlags.DeclarationOutputUnchanged
1128+
resultFlags
11401129
);
11411130
return { emitSkipped: false, diagnostics: emitDiagnostics };
11421131
}
@@ -1718,13 +1707,24 @@ namespace ts {
17181707
return actual;
17191708
}
17201709

1721-
function updateOutputTimestampsWorker(state: SolutionBuilderState, proj: ParsedCommandLine, anyDtsChange: boolean, verboseMessage: DiagnosticMessage, newestDeclarationFileContentChangedTime?: Date, skipOutputs?: ESMap<Path, string>) {
1722-
if (proj.options.noEmit) return undefined;
1723-
1710+
function updateOutputTimestampsWorker(
1711+
state: SolutionBuilderState,
1712+
proj: ParsedCommandLine,
1713+
verboseMessage: DiagnosticMessage,
1714+
skipOutputs?: ESMap<Path, string>
1715+
) {
1716+
if (proj.options.noEmit) return;
17241717
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(proj.options);
1718+
if (buildInfoPath) {
1719+
if (!skipOutputs?.has(toPath(state, buildInfoPath))) {
1720+
if (!!state.options.verbose) reportStatus(state, verboseMessage, proj.options.configFilePath!);
1721+
state.host.setModifiedTime(buildInfoPath, getCurrentTime(state.host));
1722+
}
1723+
return;
1724+
}
1725+
17251726
const { host } = state;
17261727
const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames());
1727-
const isOutFile = outFile(proj.options);
17281728
if (!skipOutputs || outputs.length !== skipOutputs.size) {
17291729
let reportVerbose = !!state.options.verbose;
17301730
let now: Date | undefined;
@@ -1733,26 +1733,18 @@ namespace ts {
17331733
continue;
17341734
}
17351735

1736-
if (proj.options.composite && isOutFile && !anyDtsChange && isDeclarationFileName(file)) {
1737-
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, file));
1736+
if (reportVerbose) {
1737+
reportVerbose = false;
1738+
reportStatus(state, verboseMessage, proj.options.configFilePath!);
17381739
}
17391740

1740-
if (!buildInfoPath || file === buildInfoPath) {
1741-
if (reportVerbose) {
1742-
reportVerbose = false;
1743-
reportStatus(state, verboseMessage, proj.options.configFilePath!);
1744-
}
1745-
1746-
host.setModifiedTime(file, now ||= getCurrentTime(state.host));
1747-
}
1741+
host.setModifiedTime(file, now ||= getCurrentTime(state.host));
17481742
}
17491743
}
1750-
1751-
return newestDeclarationFileContentChangedTime;
17521744
}
17531745

17541746
function getDtsChangeTime(state: SolutionBuilderState, options: CompilerOptions, resolvedConfigPath: ResolvedConfigFilePath) {
1755-
if (!options.composite || outFile(options)) return undefined;
1747+
if (!options.composite) return undefined;
17561748
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options)!;
17571749
const buildInfo = getBuildInfo(state, buildInfoPath, resolvedConfigPath, /*modifiedTime*/ undefined);
17581750
return buildInfo?.program?.dtsChangeTime ? new Date(buildInfo.program.dtsChangeTime) : undefined;
@@ -1762,10 +1754,10 @@ namespace ts {
17621754
if (state.options.dry) {
17631755
return reportStatus(state, Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!);
17641756
}
1765-
const priorNewestUpdateTime = updateOutputTimestampsWorker(state, proj, /*anyDtsChange*/ false, Diagnostics.Updating_output_timestamps_of_project_0);
1757+
updateOutputTimestampsWorker(state, proj, Diagnostics.Updating_output_timestamps_of_project_0);
17661758
state.projectStatus.set(resolvedPath, {
17671759
type: UpToDateStatusType.UpToDate,
1768-
newestDeclarationFileContentChangedTime: priorNewestUpdateTime || getDtsChangeTime(state, proj.options, resolvedPath),
1760+
newestDeclarationFileContentChangedTime: getDtsChangeTime(state, proj.options, resolvedPath),
17691761
oldestOutputFileName: getFirstProjectOutput(proj, !state.host.useCaseSensitiveFileNames())
17701762
});
17711763
}

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6422,6 +6422,7 @@ namespace ts {
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
64246424
affectsDeclarationPath?: true; // true if the options affects declaration file path computed
6425+
affectsOutFileBuildInfo?: true; // true if this options should be emitted in buildInfo with --out
64256426
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
64266427
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
64276428
}

src/compiler/watchPublic.ts

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ namespace ts {
77
getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
88
}
99
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
10-
if (outFile(compilerOptions)) return undefined;
1110
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(compilerOptions);
1211
if (!buildInfoPath) return undefined;
1312
let buildInfo;

src/testRunner/unittests/tsbuild/helpers.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,28 @@ interface Symbol {
200200
type ReadableProgramBuilderInfoFilePendingEmit = [string, "DtsOnly" | "Full"];
201201
type ReadableProgramBuildInfoEmitSignature = string | [string, string];
202202
interface ReadableProgramBuildInfo {
203-
fileNames: readonly string[];
203+
fileNames: readonly string[] | undefined;
204204
fileNamesList: readonly (readonly string[])[] | undefined;
205-
fileInfos: MapLike<BuilderState.FileInfo>;
205+
fileInfos: MapLike<BuilderState.FileInfo> | undefined;
206206
options: CompilerOptions | undefined;
207207
referencedMap?: MapLike<string[]>;
208208
exportedModulesMap?: MapLike<string[]>;
209209
semanticDiagnosticsPerFile?: readonly ReadableProgramBuildInfoDiagnostic[];
210210
affectedFilesPendingEmit?: readonly ReadableProgramBuilderInfoFilePendingEmit[];
211211
changeFileSet?: readonly string[];
212212
emitSignatures?: readonly ReadableProgramBuildInfoEmitSignature[];
213+
outSignature?: string;
213214
dtsChangeTime?: number;
214215
}
215216
type ReadableBuildInfo = Omit<BuildInfo, "program"> & { program: ReadableProgramBuildInfo | undefined; size: number; };
216217
function generateBuildInfoProgramBaseline(sys: System, buildInfoPath: string, buildInfo: BuildInfo) {
217218
const fileInfos: ReadableProgramBuildInfo["fileInfos"] = {};
218-
buildInfo.program?.fileInfos.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toBuilderStateFileInfo(fileInfo));
219+
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toBuilderStateFileInfo(fileInfo));
219220
const fileNamesList = buildInfo.program?.fileIdsList?.map(fileIdsListId => fileIdsListId.map(toFileName));
220221
const program: ReadableProgramBuildInfo | undefined = buildInfo.program && {
221222
fileNames: buildInfo.program.fileNames,
222223
fileNamesList,
223-
fileInfos,
224+
fileInfos: buildInfo.program.fileInfos ? fileInfos : undefined,
224225
options: buildInfo.program.options,
225226
referencedMap: toMapOfReferencedSet(buildInfo.program.referencedMap),
226227
exportedModulesMap: toMapOfReferencedSet(buildInfo.program.exportedModulesMap),
@@ -241,6 +242,7 @@ interface Symbol {
241242
toFileName(s) :
242243
[toFileName(s[0]), s[1]]
243244
),
245+
outSignature: buildInfo.program.outSignature,
244246
dtsChangeTime: buildInfo.program.dtsChangeTime,
245247
};
246248
const version = buildInfo.version === ts.version ? fakes.version : buildInfo.version;
@@ -254,7 +256,7 @@ interface Symbol {
254256
sys.writeFile(`${buildInfoPath}.readable.baseline.txt`, JSON.stringify(result, /*replacer*/ undefined, 2));
255257

256258
function toFileName(fileId: ProgramBuildInfoFileId) {
257-
return buildInfo.program!.fileNames[fileId - 1];
259+
return buildInfo.program!.fileNames![fileId - 1];
258260
}
259261

260262
function toFileNames(fileIdsListId: ProgramBuildInfoFileIdListId) {

0 commit comments

Comments
 (0)