Skip to content

Report buildInfo size in extendedDiagnostics #50293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
WriteFileCallback,
WriteFileCallbackData,
} from "./_namespaces/ts";
import * as performance from "./_namespaces/ts.performance";

/** @internal */
export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation {
Expand Down Expand Up @@ -966,10 +967,18 @@ export function isProgramBundleEmitBuildInfo(info: ProgramBuildInfo): info is Pr
return !!outFile(info.options || {});
}

function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | undefined) {
performance.mark("beforeGetProgramBuildInfo");
const result = getBuildInfoWorker(state, bundle);
performance.mark("afterGetProgramBuildInfo");
performance.measure("BuildInfo generation", "beforeGetProgramBuildInfo", "afterGetProgramBuildInfo");
return result;
}

/**
* Gets the program information to be emitted in buildInfo so that we can use it to create new program
*/
function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | undefined): BuildInfo {
function getBuildInfoWorker(state: BuilderProgramState, bundle: BundleBuildInfo | undefined): BuildInfo {
const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory();
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory));
// Convert the file name to Path here if we set the fileName instead to optimize multiple d.ts file emits and having to compute Canonical path
Expand Down
10 changes: 8 additions & 2 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,8 +829,13 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
return;
}
const buildInfo = host.getBuildInfo(bundle) || createBuildInfo(/*program*/ undefined, bundle);
performance.mark("beforeBuildInfoStringify");
const buildInfoText = getBuildInfoText(buildInfo);
performance.mark("afterBuildInfoStringify");
performance.measure("BuildInfo stringify", "beforeBuildInfoStringify", "afterBuildInfoStringify");
host.buildInfoCallbacks?.onWrite(buildInfoText.length);
// Pass buildinfo as additional data to avoid having to reparse
writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo });
writeFile(host, emitterDiagnostics, buildInfoPath, buildInfoText, /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo });
}

function emitJsFileOrBundle(
Expand Down Expand Up @@ -1229,7 +1234,7 @@ function emitUsingBuildInfoWorker(
): EmitUsingBuildInfoResult {
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
// If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo
const buildInfo = host.getBuildInfo!(buildInfoPath!, config.options.configFilePath);
const buildInfo = host.getBuildInfo!(buildInfoPath!, config.options);
if (!buildInfo) return buildInfoPath!;
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationFilePath && !buildInfo.bundle.dts)) return buildInfoPath!;

Expand Down Expand Up @@ -1330,6 +1335,7 @@ function emitUsingBuildInfoWorker(
redirectTargetsMap: createMultiMap(),
getFileIncludeReasons: notImplemented,
createHash: maybeBind(host, host.createHash),
buildInfoCallbacks: host.buildInfoCallbacks,
};
emitFiles(
notImplementedResolver,
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7514,11 +7514,14 @@ export function createInputFilesWithFilePaths(
const getAndCacheBuildInfo = () => {
if (buildInfo === undefined && buildInfoPath) {
if (host?.getBuildInfo) {
buildInfo = host.getBuildInfo(buildInfoPath, options!.configFilePath) ?? false;
buildInfo = host.getBuildInfo(buildInfoPath, options!) ?? false;
}
else {
host?.buildInfoCallbacks?.onReadStart(options);
const result = textGetter(buildInfoPath);
host?.buildInfoCallbacks?.onReadText(result);
buildInfo = result !== undefined ? getBuildInfo(buildInfoPath, result) ?? false : false;
host?.buildInfoCallbacks?.onReadEnd();
}
}
return buildInfo || undefined;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
redirectTargetsMap,
getFileIncludeReasons: program.getFileIncludeReasons,
createHash: maybeBind(host, host.createHash),
buildInfoCallbacks: host.buildInfoCallbacks,
};
}

Expand Down
31 changes: 22 additions & 9 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,9 @@ export interface SolutionBuilderHostBase<T extends BuilderProgram> extends Progr

// TODO: To do better with watch mode and normal build mode api that creates program and emits files
// This currently helps enable --diagnostics and --extendedDiagnostics
afterProgramEmitAndDiagnostics?(program: T): void;
/** @deprecated @internal */ beforeEmitBundle?(config: ParsedCommandLine): void;
/** @deprecated @internal */ afterEmitBundle?(config: ParsedCommandLine): void;
afterProgramEmitAndDiagnostics?(program: T, host?: CompilerHost): void;
/** @deprecated @internal */ beforeEmitBundle?(config: ParsedCommandLine, host: CompilerHost): void;
/** @deprecated @internal */ afterEmitBundle?(config: ParsedCommandLine, host: CompilerHost): void;

// For testing
/** @internal */ now?(): Date;
Expand Down Expand Up @@ -465,7 +465,7 @@ function createSolutionBuilderState<T extends BuilderProgram>(watch: boolean, ho
createTypeReferenceResolutionLoader,
);
}
compilerHost.getBuildInfo = (fileName, configFilePath) => getBuildInfo(state, fileName, toResolvedConfigFilePath(state, configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined);
compilerHost.getBuildInfo = (fileName, options) => getBuildInfo(state, fileName, options, toResolvedConfigFilePath(state, options.configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined);

const { watchFile, watchDirectory, writeLog } = createWatchFactory<ResolvedConfigFileName>(hostWithWatch, options);

Expand Down Expand Up @@ -1125,6 +1125,8 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
// Don't emit .d.ts if there are decl file errors
if (declDiagnostics) {
program.restoreEmitState(saved);
// Revert buildInfo write size
state.compilerHost.buildInfoCallbacks?.revertLastWrite();
({ buildResult, step } = buildErrors(
state,
projectPath,
Expand All @@ -1142,6 +1144,7 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(

// Actual Emit
const { host, compilerHost } = state;
compilerHost.buildInfoCallbacks?.clearLastWrite();
const resultFlags = program.hasChangedEmitSignature?.() ? BuildResultFlags.None : BuildResultFlags.DeclarationOutputUnchanged;
const emitterDiagnostics = createDiagnosticCollection();
const emittedOutputs = new Map<Path, string>();
Expand Down Expand Up @@ -1244,7 +1247,7 @@ function createBuildOrUpdateInvalidedProject<T extends BuilderProgram>(
// Update js, and source map
const { compilerHost } = state;
state.projectCompilerOptions = config.options;
state.host.beforeEmitBundle?.(config);
state.host.beforeEmitBundle?.(config, compilerHost);
const outputFiles = emitUsingBuildInfo(
config,
compilerHost,
Expand Down Expand Up @@ -1525,12 +1528,12 @@ function afterProgramDone<T extends BuilderProgram>(
if (program) {
if (state.write) listFiles(program, state.write);
if (state.host.afterProgramEmitAndDiagnostics) {
state.host.afterProgramEmitAndDiagnostics(program);
state.host.afterProgramEmitAndDiagnostics(program, state.compilerHost);
}
program.releaseProgram();
}
else if (state.host.afterEmitBundle) {
state.host.afterEmitBundle(config);
state.host.afterEmitBundle(config, state.compilerHost);
}
state.projectCompilerOptions = state.baseCompilerOptions;
}
Expand Down Expand Up @@ -1653,14 +1656,24 @@ function getBuildInfoCacheEntry<T extends BuilderProgram>(state: SolutionBuilder
return existing?.path === path ? existing : undefined;
}

function getBuildInfo<T extends BuilderProgram>(state: SolutionBuilderState<T>, buildInfoPath: string, resolvedConfigPath: ResolvedConfigFilePath, modifiedTime: Date | undefined): BuildInfo | undefined {
function getBuildInfo<T extends BuilderProgram>(
state: SolutionBuilderState<T>,
buildInfoPath: string,
options: CompilerOptions,
resolvedConfigPath: ResolvedConfigFilePath,
modifiedTime: Date | undefined,
): BuildInfo | undefined {
const path = toPath(state, buildInfoPath);
const existing = state.buildInfoCache.get(resolvedConfigPath);
if (existing !== undefined && existing.path === path) {
return existing.buildInfo || undefined;
}
const host = (modifiedTime ? state.host : state.compilerHost);
host.buildInfoCallbacks?.onReadStart(options);
const value = state.readFileWithCache(buildInfoPath);
host.buildInfoCallbacks?.onReadText(value);
const buildInfo = value ? ts_getBuildInfo(buildInfoPath, value) : undefined;
host.buildInfoCallbacks?.onReadEnd();
state.buildInfoCache.set(resolvedConfigPath, { path, buildInfo: buildInfo || false, modifiedTime: modifiedTime || missingFileModifiedTime });
return buildInfo;
}
Expand Down Expand Up @@ -1750,7 +1763,7 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
};
}

const buildInfo = getBuildInfo(state, buildInfoPath, resolvedPath, buildInfoTime);
const buildInfo = getBuildInfo(state, buildInfoPath, project.options, resolvedPath, buildInfoTime);
if (!buildInfo) {
// Error reading buildInfo
return {
Expand Down
14 changes: 13 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7706,6 +7706,16 @@ export type HasInvalidatedResolutions = (sourceFile: Path) => boolean;
/** @internal */
export type HasChangedAutomaticTypeDirectiveNames = () => boolean;

/** @internal */
export interface BuildInfoCallbacks {
onReadStart(compilerOptions: CompilerOptions | undefined): void;
onReadText(text: string | undefined): void;
onReadEnd(): void;
onWrite(size: number): void;
revertLastWrite(): void;
clearLastWrite(): void;
}

export interface CompilerHost extends ModuleResolutionHost {
getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
getSourceFileByPath?(fileName: string, path: Path, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined;
Expand Down Expand Up @@ -7770,7 +7780,8 @@ export interface CompilerHost extends ModuleResolutionHost {

// For testing:
/** @internal */ storeFilesChangingSignatureDuringEmit?: boolean;
/** @internal */ getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
/** @internal */ getBuildInfo?(fileName: string, options: CompilerOptions): BuildInfo | undefined;
/** @internal */ buildInfoCallbacks?: BuildInfoCallbacks;
}

/** true if --out otherwise source file name *
Expand Down Expand Up @@ -8091,6 +8102,7 @@ export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolution
getSourceFileFromReference: Program["getSourceFileFromReference"];
readonly redirectTargetsMap: RedirectTargetsMap;
createHash?(data: string): string;
buildInfoCallbacks: BuildInfoCallbacks | undefined;
}

/** @internal */
Expand Down
1 change: 1 addition & 0 deletions src/compiler/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCom
createHash: maybeBind(host, host.createHash),
readDirectory: maybeBind(host, host.readDirectory),
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
buildInfoCallbacks: host.buildInfoCallbacks,
};
return compilerHost;
}
Expand Down
17 changes: 11 additions & 6 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
BuilderProgram,
BuildInfo,
BuildInfoCallbacks,
canJsonReportNoInputFiles,
changeCompilerHostLikeToUseCache,
changesAffectModuleResolution,
Expand Down Expand Up @@ -98,20 +99,23 @@ export interface ReadBuildProgramHost {
getCurrentDirectory(): string;
readFile(fileName: string): string | undefined;
/** @internal */
getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
getBuildInfo?(fileName: string, options: CompilerOptions): BuildInfo | undefined;
/** @internal */ buildInfoCallbacks?: BuildInfoCallbacks;
}
export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadBuildProgramHost) {
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(compilerOptions);
if (!buildInfoPath) return undefined;
let buildInfo;
if (host.getBuildInfo) {
// host provides buildinfo, get it from there. This allows host to cache it
buildInfo = host.getBuildInfo(buildInfoPath, compilerOptions.configFilePath);
buildInfo = host.getBuildInfo(buildInfoPath, compilerOptions);
}
else {
host.buildInfoCallbacks?.onReadStart(compilerOptions);
const content = host.readFile(buildInfoPath);
if (!content) return undefined;
buildInfo = getBuildInfo(buildInfoPath, content);
host.buildInfoCallbacks?.onReadText(content);
buildInfo = content ? getBuildInfo(buildInfoPath, content) : undefined;
host.buildInfoCallbacks?.onReadEnd();
}
if (!buildInfo || buildInfo.version !== version || !buildInfo.program) return undefined;
return createBuilderProgramUsingProgramBuildInfo(buildInfo, buildInfoPath, host);
Expand Down Expand Up @@ -245,6 +249,7 @@ export interface ProgramHost<T extends BuilderProgram> {
// TODO: GH#18217 Optional methods are frequently asserted
createDirectory?(path: string): void;
writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
buildInfoCallbacks?: BuildInfoCallbacks;
// For testing
storeFilesChangingSignatureDuringEmit?: boolean;
now?(): Date;
Expand All @@ -258,7 +263,7 @@ export interface WatchCompilerHost<T extends BuilderProgram> extends ProgramHost
getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined;

/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
afterProgramCreate?(program: T, host?: CompilerHost): void;
}

/**
Expand Down Expand Up @@ -611,7 +616,7 @@ export function createWatchProgram<T extends BuilderProgram>(host: WatchCompiler

reportFileChangeDetectedOnCreateProgram = false;
if (host.afterProgramCreate && program !== builderProgram) {
host.afterProgramCreate(builderProgram);
host.afterProgramCreate(builderProgram, compilerHost);
}

compilerHost.readFile = originalReadFile;
Expand Down
Loading