Skip to content

Commit

Permalink
Store dts time for --out in the buildInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
sheetalkamat committed Apr 19, 2022
1 parent dc21283 commit 5bccee8
Show file tree
Hide file tree
Showing 56 changed files with 3,639 additions and 1,445 deletions.
270 changes: 148 additions & 122 deletions src/compiler/builder.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ namespace ts {
type: "string",
affectsEmit: true,
affectsDeclarationPath: true,
affectsOutFileBuildInfo: true,
isFilePath: true,
paramType: Diagnostics.FILE,
showInSimplifiedHelpView: true,
Expand Down Expand Up @@ -556,6 +557,7 @@ namespace ts {
name: "composite",
type: "boolean",
affectsEmit: true,
affectsOutFileBuildInfo: true,
isTSConfigOnly: true,
category: Diagnostics.Projects,
transpileOptionValue: undefined,
Expand All @@ -566,6 +568,7 @@ namespace ts {
name: "tsBuildInfoFile",
type: "string",
affectsEmit: true,
affectsOutFileBuildInfo: true,
isFilePath: true,
paramType: Diagnostics.FILE,
category: Diagnostics.Projects,
Expand Down Expand Up @@ -992,6 +995,7 @@ namespace ts {
type: "string",
affectsEmit: true,
affectsDeclarationPath: true,
affectsOutFileBuildInfo: true,
isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files
// for correct behaviour, please use outFile
category: Diagnostics.Backwards_Compatibility,
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ namespace ts {
getCanonicalFileName(fileName: string): string;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
createHash?(data: string): string;
now?(): Date;
}

function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
Expand Down Expand Up @@ -780,6 +782,8 @@ namespace ts {
const outputFiles: OutputFile[] = [];
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
let changedDtsText: string | undefined;
let changedDtsData: WriteFileCallbackData | undefined;
const emitHost: EmitHost = {
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
getCanonicalFileName: host.getCanonicalFileName,
Expand All @@ -806,6 +810,11 @@ namespace ts {
case buildInfoPath:
const newBuildInfo = data!.buildInfo!;
newBuildInfo.program = buildInfo.program;
if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
// Update the output signature
newBuildInfo.program.outSignature = computeSignature(changedDtsText, changedDtsData, maybeBind(host, host.createHash));
newBuildInfo.program.dtsChangeTime = getCurrentTime(host).getTime();
}
// Update sourceFileInfo
const { js, dts, sourceFiles } = buildInfo.bundle!;
newBuildInfo.bundle!.js!.sources = js!.sources;
Expand All @@ -817,6 +826,8 @@ namespace ts {
return;
case declarationFilePath:
if (declarationText === text) return;
changedDtsText = text;
changedDtsData = data;
break;
case declarationMapPath:
if (declarationMapText === text) return;
Expand Down
86 changes: 39 additions & 47 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ namespace ts {
return getOrCreateValueFromConfigFileMap(configFileMap, resolved, () => new Map());
}

function newer(date1: Date | undefined, date2: Date): Date | undefined {
return date1 ? date2 > date1 ? date2 : date1 : date2;
}

/*@internal*/
export function getCurrentTime(host: { now?(): Date; }) {
return host.now ? host.now() : new Date();
Expand Down Expand Up @@ -987,33 +983,23 @@ namespace ts {
// Actual Emit
const { host, compilerHost } = state;
let resultFlags = BuildResultFlags.DeclarationOutputUnchanged;
let newestDeclarationFileContentChangedTime: Date | undefined;
const existingBuildInfo = state.buildInfoCache.get(projectPath)?.buildInfo || undefined;
const emitterDiagnostics = createDiagnosticCollection();
const emittedOutputs = new Map<Path, string>();
const isOutFile = outFile(config.options);
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
if (resultFlags === BuildResultFlags.DeclarationOutputUnchanged && isDeclarationFileName(name)) {
// Check for unchanged .d.ts files
if (state.readFileWithCache(name) === text) {
if (config.options.composite && isOutFile) {
newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, ts.getModifiedTime(host, name));
}
}
else {
emittedOutputs.set(toPath(state, name), name);
if (buildInfo) {
setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
if (buildInfo.program?.dtsChangeTime !== existingBuildInfo?.program?.dtsChangeTime) {
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
}
}

const path = toPath(state, name);
emittedOutputs.set(path, name);
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, program!.getCompilerOptions());
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
});

finishEmit(
emitterDiagnostics,
emittedOutputs,
newestDeclarationFileContentChangedTime,
outputFiles.length ? outputFiles[0].name : getFirstProjectOutput(config, !host.useCaseSensitiveFileNames()),
resultFlags
);
Expand Down Expand Up @@ -1045,7 +1031,6 @@ namespace ts {
function finishEmit(
emitterDiagnostics: DiagnosticCollection,
emittedOutputs: ESMap<Path, string>,
newestDeclarationFileContentChangedTime: Date | undefined,
oldestOutputFileName: string,
resultFlags: BuildResultFlags
) {
Expand All @@ -1068,12 +1053,11 @@ namespace ts {
}

// Update time stamps for rest of the outputs
const anyDtsChange = !(resultFlags & BuildResultFlags.DeclarationOutputUnchanged);
newestDeclarationFileContentChangedTime = updateOutputTimestampsWorker(state, config, anyDtsChange, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, newestDeclarationFileContentChangedTime, emittedOutputs);
updateOutputTimestampsWorker(state, config, Diagnostics.Updating_unchanged_output_timestamps_of_project_0, emittedOutputs);
state.diagnostics.delete(projectPath);
state.projectStatus.set(projectPath, {
type: UpToDateStatusType.UpToDate,
newestDeclarationFileContentChangedTime: newestDeclarationFileContentChangedTime || getDtsChangeTime(state, config.options, projectPath),
newestDeclarationFileContentChangedTime: getDtsChangeTime(state, config.options, projectPath),
oldestOutputFileName
});
afterProgramDone(state, program, config);
Expand Down Expand Up @@ -1124,19 +1108,24 @@ namespace ts {
Debug.assert(!!outputFiles.length);
const emitterDiagnostics = createDiagnosticCollection();
const emittedOutputs = new Map<Path, string>();
let resultFlags = BuildResultFlags.DeclarationOutputUnchanged;
const existingBuildInfo = state.buildInfoCache.get(projectPath)!.buildInfo as BuildInfo;
outputFiles.forEach(({ name, text, writeByteOrderMark, buildInfo }) => {
const path = toPath(state, name);
emittedOutputs.set(path, name);
if (buildInfo) setBuildInfo(state, buildInfo, projectPath, config.options);
emittedOutputs.set(toPath(state, name), name);
if (buildInfo) {
setBuildInfo(state, buildInfo, projectPath, config.options);
if (buildInfo.program?.dtsChangeTime !== existingBuildInfo.program?.dtsChangeTime) {
resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged;
}
}
writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark);
});

const emitDiagnostics = finishEmit(
emitterDiagnostics,
emittedOutputs,
/*newestDeclarationFileContentChangedTime*/ undefined,
outputFiles[0].name,
BuildResultFlags.DeclarationOutputUnchanged
resultFlags
);
return { emitSkipped: false, diagnostics: emitDiagnostics };
}
Expand Down Expand Up @@ -1718,13 +1707,24 @@ namespace ts {
return actual;
}

function updateOutputTimestampsWorker(state: SolutionBuilderState, proj: ParsedCommandLine, anyDtsChange: boolean, verboseMessage: DiagnosticMessage, newestDeclarationFileContentChangedTime?: Date, skipOutputs?: ESMap<Path, string>) {
if (proj.options.noEmit) return undefined;

function updateOutputTimestampsWorker(
state: SolutionBuilderState,
proj: ParsedCommandLine,
verboseMessage: DiagnosticMessage,
skipOutputs?: ESMap<Path, string>
) {
if (proj.options.noEmit) return;
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(proj.options);
if (buildInfoPath) {
if (!skipOutputs?.has(toPath(state, buildInfoPath))) {
if (!!state.options.verbose) reportStatus(state, verboseMessage, proj.options.configFilePath!);
state.host.setModifiedTime(buildInfoPath, getCurrentTime(state.host));
}
return;
}

const { host } = state;
const outputs = getAllProjectOutputs(proj, !host.useCaseSensitiveFileNames());
const isOutFile = outFile(proj.options);
if (!skipOutputs || outputs.length !== skipOutputs.size) {
let reportVerbose = !!state.options.verbose;
let now: Date | undefined;
Expand All @@ -1733,26 +1733,18 @@ namespace ts {
continue;
}

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

if (!buildInfoPath || file === buildInfoPath) {
if (reportVerbose) {
reportVerbose = false;
reportStatus(state, verboseMessage, proj.options.configFilePath!);
}

host.setModifiedTime(file, now ||= getCurrentTime(state.host));
}
host.setModifiedTime(file, now ||= getCurrentTime(state.host));
}
}

return newestDeclarationFileContentChangedTime;
}

function getDtsChangeTime(state: SolutionBuilderState, options: CompilerOptions, resolvedConfigPath: ResolvedConfigFilePath) {
if (!options.composite || outFile(options)) return undefined;
if (!options.composite) return undefined;
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options)!;
const buildInfo = getBuildInfo(state, buildInfoPath, resolvedConfigPath, /*modifiedTime*/ undefined);
return buildInfo?.program?.dtsChangeTime ? new Date(buildInfo.program.dtsChangeTime) : undefined;
Expand All @@ -1762,10 +1754,10 @@ namespace ts {
if (state.options.dry) {
return reportStatus(state, Diagnostics.A_non_dry_build_would_update_timestamps_for_output_of_project_0, proj.options.configFilePath!);
}
const priorNewestUpdateTime = updateOutputTimestampsWorker(state, proj, /*anyDtsChange*/ false, Diagnostics.Updating_output_timestamps_of_project_0);
updateOutputTimestampsWorker(state, proj, Diagnostics.Updating_output_timestamps_of_project_0);
state.projectStatus.set(resolvedPath, {
type: UpToDateStatusType.UpToDate,
newestDeclarationFileContentChangedTime: priorNewestUpdateTime || getDtsChangeTime(state, proj.options, resolvedPath),
newestDeclarationFileContentChangedTime: getDtsChangeTime(state, proj.options, resolvedPath),
oldestOutputFileName: getFirstProjectOutput(proj, !state.host.useCaseSensitiveFileNames())
});
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6422,6 +6422,7 @@ namespace ts {
affectsEmit?: true; // true if the options affects emit
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
affectsDeclarationPath?: true; // true if the options affects declaration file path computed
affectsOutFileBuildInfo?: true; // true if this options should be emitted in buildInfo with --out
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
}
Expand Down
1 change: 0 additions & 1 deletion src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace ts {
getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
if (outFile(compilerOptions)) return undefined;
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(compilerOptions);
if (!buildInfoPath) return undefined;
let buildInfo;
Expand Down
12 changes: 7 additions & 5 deletions src/testRunner/unittests/tsbuild/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,27 +200,28 @@ interface Symbol {
type ReadableProgramBuilderInfoFilePendingEmit = [string, "DtsOnly" | "Full"];
type ReadableProgramBuildInfoEmitSignature = string | [string, string];
interface ReadableProgramBuildInfo {
fileNames: readonly string[];
fileNames: readonly string[] | undefined;
fileNamesList: readonly (readonly string[])[] | undefined;
fileInfos: MapLike<BuilderState.FileInfo>;
fileInfos: MapLike<BuilderState.FileInfo> | undefined;
options: CompilerOptions | undefined;
referencedMap?: MapLike<string[]>;
exportedModulesMap?: MapLike<string[]>;
semanticDiagnosticsPerFile?: readonly ReadableProgramBuildInfoDiagnostic[];
affectedFilesPendingEmit?: readonly ReadableProgramBuilderInfoFilePendingEmit[];
changeFileSet?: readonly string[];
emitSignatures?: readonly ReadableProgramBuildInfoEmitSignature[];
outSignature?: string;
dtsChangeTime?: number;
}
type ReadableBuildInfo = Omit<BuildInfo, "program"> & { program: ReadableProgramBuildInfo | undefined; size: number; };
function generateBuildInfoProgramBaseline(sys: System, buildInfoPath: string, buildInfo: BuildInfo) {
const fileInfos: ReadableProgramBuildInfo["fileInfos"] = {};
buildInfo.program?.fileInfos.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toBuilderStateFileInfo(fileInfo));
buildInfo.program?.fileInfos?.forEach((fileInfo, index) => fileInfos[toFileName(index + 1 as ProgramBuildInfoFileId)] = toBuilderStateFileInfo(fileInfo));
const fileNamesList = buildInfo.program?.fileIdsList?.map(fileIdsListId => fileIdsListId.map(toFileName));
const program: ReadableProgramBuildInfo | undefined = buildInfo.program && {
fileNames: buildInfo.program.fileNames,
fileNamesList,
fileInfos,
fileInfos: buildInfo.program.fileInfos ? fileInfos : undefined,
options: buildInfo.program.options,
referencedMap: toMapOfReferencedSet(buildInfo.program.referencedMap),
exportedModulesMap: toMapOfReferencedSet(buildInfo.program.exportedModulesMap),
Expand All @@ -241,6 +242,7 @@ interface Symbol {
toFileName(s) :
[toFileName(s[0]), s[1]]
),
outSignature: buildInfo.program.outSignature,
dtsChangeTime: buildInfo.program.dtsChangeTime,
};
const version = buildInfo.version === ts.version ? fakes.version : buildInfo.version;
Expand All @@ -254,7 +256,7 @@ interface Symbol {
sys.writeFile(`${buildInfoPath}.readable.baseline.txt`, JSON.stringify(result, /*replacer*/ undefined, 2));

function toFileName(fileId: ProgramBuildInfoFileId) {
return buildInfo.program!.fileNames[fileId - 1];
return buildInfo.program!.fileNames![fileId - 1];
}

function toFileNames(fileIdsListId: ProgramBuildInfoFileIdListId) {
Expand Down
Loading

0 comments on commit 5bccee8

Please sign in to comment.