Avoid unnecessary buildInfo read if host supports caching it (avoids in --build scenario) and some reporting cleanup (#51403)

* Emit diagnostics when just manipuating bundle at that time itself
[4:04:42 PM] Updating output of project '/TypeScript/src/tsserver/tsconfig.json'...

Memory used:        581215K
transformTime time:   0.01s
Source Map time:      0.35s
commentTime time:     0.00s
printTime time:       0.58s
Emit time:            0.90s
I/O Write time:       0.01s
Total time:           0.90s

* Pull out getSourceFile and writeFile in a function

* Fix incorrect solutionPerformance reporting in watch mode

* Remove unnecessary build info read when host can give cached buildInfo

* Simplify overloads

* Accept API change
This commit is contained in:
Sheetal Nandi 2022-11-04 19:19:57 -07:00 committed by GitHub
parent f0216e3421
commit 354891cf43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 275 additions and 250 deletions

View File

@ -705,17 +705,7 @@ namespace ts {
/** File that isnt present resulting in error or output files */
export type EmitUsingBuildInfoResult = string | readonly OutputFile[];
/*@internal*/
export interface EmitUsingBuildInfoHost extends ModuleResolutionHost {
getCurrentDirectory(): string;
getCanonicalFileName(fileName: string): string;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
createHash?(data: string): string;
getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
}
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: CompilerHost): readonly SourceFile[] {
const jsBundle = Debug.checkDefined(bundle.js);
const prologueMap = jsBundle.sources?.prologues && arrayToMap(jsBundle.sources.prologues, prologueInfo => prologueInfo.file);
return bundle.sourceFiles.map((fileName, index) => {
@ -745,22 +735,29 @@ namespace ts {
/*@internal*/
export function emitUsingBuildInfo(
config: ParsedCommandLine,
host: EmitUsingBuildInfoHost,
host: CompilerHost,
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
customTransformers?: CustomTransformers
): EmitUsingBuildInfoResult {
tracing?.push(tracing.Phase.Emit, "emitUsingBuildInfo", {}, /*separateBeginAndEnd*/ true);
performance.mark("beforeEmit");
const result = emitUsingBuildInfoWorker(config, host, getCommandLine, customTransformers);
performance.mark("afterEmit");
performance.measure("Emit", "beforeEmit", "afterEmit");
tracing?.pop();
return result;
}
function emitUsingBuildInfoWorker(
config: ParsedCommandLine,
host: CompilerHost,
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
customTransformers?: CustomTransformers
): EmitUsingBuildInfoResult {
const createHash = maybeBind(host, host.createHash);
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
let buildInfo: BuildInfo | undefined;
if (host.getBuildInfo) {
// If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo
buildInfo = host.getBuildInfo(buildInfoPath!, config.options.configFilePath);
}
else {
const buildInfoText = host.readFile(buildInfoPath!);
if (!buildInfoText) return buildInfoPath!;
buildInfo = getBuildInfo(buildInfoPath!, buildInfoText);
}
// 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);
if (!buildInfo) return buildInfoPath!;
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationFilePath && !buildInfo.bundle.dts)) return buildInfoPath!;
@ -783,28 +780,28 @@ namespace ts {
if (declarationMapPath && computeSignature(declarationMapText!, createHash) !== buildInfo.bundle.dts!.mapHash) return declarationMapPath;
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
const ownPrependInput = createInputFiles(
const ownPrependInput = createInputFilesWithFileTexts(
jsFilePath,
jsFileText,
declarationText!,
sourceMapFilePath,
sourceMapText,
declarationFilePath,
declarationText!,
declarationMapPath,
declarationMapText,
jsFilePath,
declarationFilePath,
buildInfoPath,
buildInfo,
/*onlyOwnText*/ true
);
const outputFiles: OutputFile[] = [];
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f), host);
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,
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo!.bundle!.commonSourceDirectory, buildInfoDirectory),
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo.bundle!.commonSourceDirectory, buildInfoDirectory),
getCompilerOptions: () => config.options,
getCurrentDirectory: () => host.getCurrentDirectory(),
getNewLine: () => host.getNewLine(),
@ -826,13 +823,13 @@ namespace ts {
break;
case buildInfoPath:
const newBuildInfo = data!.buildInfo!;
newBuildInfo.program = buildInfo!.program;
newBuildInfo.program = buildInfo.program;
if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
// Update the output signature
(newBuildInfo.program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData);
}
// Update sourceFileInfo
const { js, dts, sourceFiles } = buildInfo!.bundle!;
const { js, dts, sourceFiles } = buildInfo.bundle!;
newBuildInfo.bundle!.js!.sources = js!.sources;
if (dts) {
newBuildInfo.bundle!.dts!.sources = dts.sources;

View File

@ -6742,6 +6742,14 @@ namespace ts {
javascriptText: string,
declarationText: string
): InputFiles;
export function createInputFiles(
javascriptText: string,
declarationText: string,
javascriptMapPath: string | undefined,
javascriptMapText: string | undefined,
declarationMapPath: string | undefined,
declarationMapText: string | undefined
): InputFiles;
export function createInputFiles(
readFileText: (path: string) => string | undefined,
javascriptPath: string,
@ -6750,28 +6758,6 @@ namespace ts {
declarationMapPath: string | undefined,
buildInfoPath: string | undefined
): InputFiles;
export function createInputFiles(
javascriptText: string,
declarationText: string,
javascriptMapPath: string | undefined,
javascriptMapText: string | undefined,
declarationMapPath: string | undefined,
declarationMapText: string | undefined
): InputFiles;
/*@internal*/
export function createInputFiles(
javascriptText: string,
declarationText: string,
javascriptMapPath: string | undefined,
javascriptMapText: string | undefined,
declarationMapPath: string | undefined,
declarationMapText: string | undefined,
javascriptPath: string | undefined,
declarationPath: string | undefined,
buildInfoPath?: string | undefined,
buildInfo?: BuildInfo,
oldFileOfCurrentEmit?: boolean
): InputFiles;
export function createInputFiles(
javascriptTextOrReadFileText: string | ((path: string) => string | undefined),
declarationTextOrJavascriptPath: string,
@ -6779,62 +6765,106 @@ namespace ts {
javascriptMapTextOrDeclarationPath?: string,
declarationMapPath?: string,
declarationMapTextOrBuildInfoPath?: string,
javascriptPath?: string | undefined,
declarationPath?: string | undefined,
buildInfoPath?: string | undefined,
buildInfo?: BuildInfo,
oldFileOfCurrentEmit?: boolean
): InputFiles {
return !isString(javascriptTextOrReadFileText) ?
createInputFilesWithFilePaths(
javascriptTextOrReadFileText,
declarationTextOrJavascriptPath,
javascriptMapPath,
javascriptMapTextOrDeclarationPath!,
declarationMapPath,
declarationMapTextOrBuildInfoPath,
) :
createInputFilesWithFileTexts(
/*javascriptPath*/ undefined,
javascriptTextOrReadFileText,
javascriptMapPath,
javascriptMapTextOrDeclarationPath,
/*declarationPath*/ undefined,
declarationTextOrJavascriptPath,
declarationMapPath,
declarationMapTextOrBuildInfoPath,
);
}
/*@internal*/
export function createInputFilesWithFilePaths(
readFileText: (path: string) => string | undefined,
javascriptPath: string,
javascriptMapPath: string | undefined,
declarationPath: string,
declarationMapPath: string | undefined,
buildInfoPath: string | undefined,
host?: CompilerHost,
options?: CompilerOptions,
): InputFiles {
const node = parseNodeFactory.createInputFiles();
if (!isString(javascriptTextOrReadFileText)) {
const cache = new Map<string, string | false>();
const textGetter = (path: string | undefined) => {
if (path === undefined) return undefined;
let value = cache.get(path);
if (value === undefined) {
value = javascriptTextOrReadFileText(path);
cache.set(path, value !== undefined ? value : false);
node.javascriptPath = javascriptPath;
node.javascriptMapPath = javascriptMapPath;
node.declarationPath = declarationPath;
node.declarationMapPath = declarationMapPath;
node.buildInfoPath = buildInfoPath;
const cache = new Map<string, string | false>();
const textGetter = (path: string | undefined) => {
if (path === undefined) return undefined;
let value = cache.get(path);
if (value === undefined) {
value = readFileText(path);
cache.set(path, value !== undefined ? value : false);
}
return value !== false ? value as string : undefined;
};
const definedTextGetter = (path: string) => {
const result = textGetter(path);
return result !== undefined ? result : `/* Input file ${path} was missing */\r\n`;
};
let buildInfo: BuildInfo | false;
const getAndCacheBuildInfo = () => {
if (buildInfo === undefined && buildInfoPath) {
if (host?.getBuildInfo) {
buildInfo = host.getBuildInfo(buildInfoPath, options!.configFilePath) ?? false;
}
return value !== false ? value as string : undefined;
};
const definedTextGetter = (path: string) => {
const result = textGetter(path);
return result !== undefined ? result : `/* Input file ${path} was missing */\r\n`;
};
let buildInfo: BuildInfo | false;
const getAndCacheBuildInfo = (getText: () => string | undefined) => {
if (buildInfo === undefined) {
const result = getText();
buildInfo = result !== undefined ? getBuildInfo(node.buildInfoPath!, result) ?? false : false;
else {
const result = textGetter(buildInfoPath);
buildInfo = result !== undefined ? getBuildInfo(buildInfoPath, result) ?? false : false;
}
return buildInfo || undefined;
};
node.javascriptPath = declarationTextOrJavascriptPath;
node.javascriptMapPath = javascriptMapPath;
node.declarationPath = Debug.checkDefined(javascriptMapTextOrDeclarationPath);
node.declarationMapPath = declarationMapPath;
node.buildInfoPath = declarationMapTextOrBuildInfoPath;
Object.defineProperties(node, {
javascriptText: { get() { return definedTextGetter(declarationTextOrJavascriptPath); } },
javascriptMapText: { get() { return textGetter(javascriptMapPath); } }, // TODO:: if there is inline sourceMap in jsFile, use that
declarationText: { get() { return definedTextGetter(Debug.checkDefined(javascriptMapTextOrDeclarationPath)); } },
declarationMapText: { get() { return textGetter(declarationMapPath); } }, // TODO:: if there is inline sourceMap in dtsFile, use that
buildInfo: { get() { return getAndCacheBuildInfo(() => textGetter(declarationMapTextOrBuildInfoPath)); } }
});
}
else {
node.javascriptText = javascriptTextOrReadFileText;
node.javascriptMapPath = javascriptMapPath;
node.javascriptMapText = javascriptMapTextOrDeclarationPath;
node.declarationText = declarationTextOrJavascriptPath;
node.declarationMapPath = declarationMapPath;
node.declarationMapText = declarationMapTextOrBuildInfoPath;
node.javascriptPath = javascriptPath;
node.declarationPath = declarationPath;
node.buildInfoPath = buildInfoPath;
node.buildInfo = buildInfo;
node.oldFileOfCurrentEmit = oldFileOfCurrentEmit;
}
}
return buildInfo || undefined;
};
Object.defineProperties(node, {
javascriptText: { get: () => definedTextGetter(javascriptPath) },
javascriptMapText: { get: () => textGetter(javascriptMapPath) }, // TODO:: if there is inline sourceMap in jsFile, use that
declarationText: { get: () => definedTextGetter(Debug.checkDefined(declarationPath)) },
declarationMapText: { get: () => textGetter(declarationMapPath) }, // TODO:: if there is inline sourceMap in dtsFile, use that
buildInfo: { get: getAndCacheBuildInfo },
});
return node;
}
/*@internal*/
export function createInputFilesWithFileTexts(
javascriptPath: string | undefined,
javascriptText: string,
javascriptMapPath: string | undefined,
javascriptMapText: string | undefined,
declarationPath: string | undefined,
declarationText: string,
declarationMapPath: string | undefined,
declarationMapText: string | undefined,
buildInfoPath?: string,
buildInfo?: BuildInfo,
oldFileOfCurrentEmit?: boolean,
): InputFiles {
const node = parseNodeFactory.createInputFiles();
node.javascriptPath = javascriptPath;
node.javascriptText = javascriptText;
node.javascriptMapPath = javascriptMapPath;
node.javascriptMapText = javascriptMapText;
node.declarationPath = declarationPath;
node.declarationText = declarationText;
node.declarationMapPath = declarationMapPath;
node.declarationMapText = declarationMapText;
node.buildInfoPath = buildInfoPath;
node.buildInfo = buildInfo;
node.oldFileOfCurrentEmit = oldFileOfCurrentEmit;
return node;
}

View File

@ -63,14 +63,16 @@ namespace ts {
}
/*@internal*/
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
const existingDirectories = new Map<string, boolean>();
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
function getSourceFile(fileName: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, onError?: (message: string) => void): SourceFile | undefined {
export function createGetSourceFile(
readFile: ProgramHost<any>["readFile"],
getCompilerOptions: () => CompilerOptions,
setParentNodes: boolean | undefined
): CompilerHost["getSourceFile"] {
return (fileName, languageVersionOrOptions, onError) => {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = compilerHost.readFile(fileName);
text = readFile(fileName, getCompilerOptions().charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
@ -81,8 +83,46 @@ namespace ts {
text = "";
}
return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined;
}
};
}
/*@internal*/
export function createWriteFileMeasuringIO(
actualWriteFile: (path: string, data: string, writeByteOrderMark: boolean) => void,
createDirectory: (path: string) => void,
directoryExists: (path: string) => boolean
): CompilerHost["writeFile"] {
return (fileName, data, writeByteOrderMark, onError) => {
try {
performance.mark("beforeIOWrite");
// NOTE: If patchWriteFileEnsuringDirectory has been called,
// the system.writeFile will do its own directory creation and
// the ensureDirectoriesExist call will always be redundant.
writeFileEnsuringDirectories(
fileName,
data,
writeByteOrderMark,
actualWriteFile,
createDirectory,
directoryExists
);
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
};
}
/*@internal*/
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
const existingDirectories = new Map<string, boolean>();
const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
function directoryExists(directoryPath: string): boolean {
if (existingDirectories.has(directoryPath)) {
return true;
@ -94,31 +134,6 @@ namespace ts {
return false;
}
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
// NOTE: If patchWriteFileEnsuringDirectory has been called,
// the system.writeFile will do its own directory creation and
// the ensureDirectoriesExist call will always be redundant.
writeFileEnsuringDirectories(
fileName,
data,
writeByteOrderMark,
(path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark),
path => (compilerHost.createDirectory || system.createDirectory)(path),
path => directoryExists(path));
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
}
@ -126,10 +141,14 @@ namespace ts {
const newLine = getNewLineCharacter(options, () => system.newLine);
const realpath = system.realpath && ((path: string) => system.realpath!(path));
const compilerHost: CompilerHost = {
getSourceFile,
getSourceFile: createGetSourceFile(fileName => compilerHost.readFile(fileName), () => options, setParentNodes),
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile,
writeFile: createWriteFileMeasuringIO(
(path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark),
path => (compilerHost.createDirectory || system.createDirectory)(path),
path => directoryExists(path),
),
getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
@ -2002,7 +2021,8 @@ namespace ts {
const path = toPath(fileName);
const sourceFile = getSourceFileByPath(path);
return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path);
}
},
host,
);
}
@ -4292,7 +4312,12 @@ namespace ts {
}
/* @internal */
export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) {
export function createPrependNodes(
projectReferences: readonly ProjectReference[] | undefined,
getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined,
readFile: (path: string) => string | undefined,
host: CompilerHost,
) {
if (!projectReferences) return emptyArray;
let nodes: InputFiles[] | undefined;
for (let i = 0; i < projectReferences.length; i++) {
@ -4304,7 +4329,7 @@ namespace ts {
if (!out) continue;
const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true);
const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath);
const node = createInputFilesWithFilePaths(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath, host, resolvedRefOpts.options);
(nodes || (nodes = [])).push(node);
}
}

View File

@ -101,6 +101,7 @@ namespace ts {
// 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;
/*@internal*/ beforeEmitBundle?(config: ParsedCommandLine): void;
/*@internal*/ afterEmitBundle?(config: ParsedCommandLine): void;
// For testing
@ -253,7 +254,7 @@ namespace ts {
readonly projectPendingBuild: ESMap<ResolvedConfigFilePath, ConfigFileProgramReloadLevel>;
readonly projectErrorsReported: ESMap<ResolvedConfigFilePath, true>;
readonly compilerHost: CompilerHost;
readonly compilerHost: CompilerHost & ReadBuildProgramHost;
readonly moduleResolutionCache: ModuleResolutionCache | undefined;
readonly typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
@ -1087,6 +1088,7 @@ namespace ts {
// Update js, and source map
const { compilerHost } = state;
state.projectCompilerOptions = config.options;
state.host.beforeEmitBundle?.(config);
const outputFiles = emitUsingBuildInfo(
config,
compilerHost,

View File

@ -7234,6 +7234,7 @@ namespace ts {
// For testing:
/*@internal*/ disableUseFileVersionAsSignature?: boolean;
/*@internal*/ storeFilesChangingSignatureDuringEmit?: boolean;
/*@internal*/ getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
}
/** true if --out otherwise source file name */

View File

@ -596,26 +596,18 @@ namespace ts {
const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames();
const hostGetNewLine = memoize(() => host.getNewLine());
return {
getSourceFile: (fileName, languageVersionOrOptions, onError) => {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = host.readFile(fileName, getCompilerOptions().charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
catch (e) {
if (onError) {
onError(e.message);
}
text = "";
}
return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions) : undefined;
},
getSourceFile: createGetSourceFile(
(fileName, encoding) => host.readFile(fileName, encoding),
getCompilerOptions,
/*setParentNodes*/ undefined
),
getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation),
getDefaultLibFileName: options => host.getDefaultLibFileName(options),
writeFile,
writeFile: createWriteFileMeasuringIO(
(path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark),
path => host.createDirectory!(path),
path => host.directoryExists!(path)
),
getCurrentDirectory: memoize(() => host.getCurrentDirectory()),
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
@ -632,31 +624,6 @@ namespace ts {
disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature,
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
};
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
try {
performance.mark("beforeIOWrite");
// NOTE: If patchWriteFileEnsuringDirectory has been called,
// the host.writeFile will do its own directory creation and
// the ensureDirectoriesExist call will always be redundant.
writeFileEnsuringDirectories(
fileName,
text,
writeByteOrderMark,
(path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark),
path => host.createDirectory!(path),
path => host.directoryExists!(path));
performance.mark("afterIOWrite");
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
}
catch (e) {
if (onError) {
onError(e.message);
}
}
}
}
export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) {

View File

@ -752,15 +752,20 @@ namespace ts {
const solutionPerformance = enableSolutionPerformance(sys, buildOptions);
updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance);
const onWatchStatusChange = buildHost.onWatchStatusChange;
let reportBuildStatistics = false;
buildHost.onWatchStatusChange = (d, newLine, options, errorCount) => {
onWatchStatusChange?.(d, newLine, options, errorCount);
if (d.code === Diagnostics.Found_0_errors_Watching_for_file_changes.code ||
d.code === Diagnostics.Found_1_error_Watching_for_file_changes.code) {
if (reportBuildStatistics && (
d.code === Diagnostics.Found_0_errors_Watching_for_file_changes.code ||
d.code === Diagnostics.Found_1_error_Watching_for_file_changes.code
)) {
reportSolutionBuilderTimes(builder, solutionPerformance);
}
};
const builder = createSolutionBuilderWithWatch(buildHost, projects, buildOptions, watchOptions);
builder.build();
reportSolutionBuilderTimes(builder, solutionPerformance);
reportBuildStatistics = true;
return builder;
}
@ -855,7 +860,11 @@ namespace ts {
reportStatistics(sys, program.getProgram(), solutionPerformance);
cb(program);
};
buildHost.afterEmitBundle = cb;
buildHost.beforeEmitBundle = config => enableStatisticsAndTracing(sys, config.options, /*isBuildMode*/ true);
buildHost.afterEmitBundle = config => {
reportStatistics(sys, config, solutionPerformance);
cb(config);
};
}
function updateCreateProgram<T extends BuilderProgram>(sys: System, host: { createProgram: CreateProgram<T>; }, isBuildMode: boolean) {
@ -997,6 +1006,7 @@ namespace ts {
});
performance.disable();
performance.enable();
solutionPerformance.clear();
reportAllStatistics(sys, statistics);
@ -1035,8 +1045,14 @@ namespace ts {
return startsWith(name, "SolutionBuilder::");
}
function reportStatistics(sys: System, program: Program, solutionPerformance: SolutionPerformance | undefined) {
const compilerOptions = program.getCompilerOptions();
function isProgram(programOrConfig: Program | ParsedCommandLine): programOrConfig is Program {
return !(programOrConfig as ParsedCommandLine).options;
}
function reportStatistics(sys: System, programOrConfig: Program | ParsedCommandLine, solutionPerformance: SolutionPerformance | undefined) {
const program = isProgram(programOrConfig) ? programOrConfig : undefined;
const config = isProgram(programOrConfig) ? undefined : programOrConfig;
const compilerOptions = program ? program.getCompilerOptions() : config!.options;
if (canTrace(sys, compilerOptions)) {
tracing?.stopTracing();
@ -1046,23 +1062,24 @@ namespace ts {
if (canReportDiagnostics(sys, compilerOptions)) {
statistics = [];
const memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
if (program) {
reportCountStatistic("Files", program.getSourceFiles().length);
const lineCounts = countLines(program);
if (compilerOptions.extendedDiagnostics) {
for (const key of arrayFrom(lineCounts.keys())) {
reportCountStatistic("Lines of " + key, lineCounts.get(key)!);
const lineCounts = countLines(program);
if (compilerOptions.extendedDiagnostics) {
for (const key of arrayFrom(lineCounts.keys())) {
reportCountStatistic("Lines of " + key, lineCounts.get(key)!);
}
}
else {
reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0));
}
}
else {
reportCountStatistic("Lines", reduceLeftIterator(lineCounts.values(), (sum, count) => sum + count, 0));
}
reportCountStatistic("Identifiers", program.getIdentifierCount());
reportCountStatistic("Symbols", program.getSymbolCount());
reportCountStatistic("Types", program.getTypeCount());
reportCountStatistic("Instantiations", program.getInstantiationCount());
reportCountStatistic("Identifiers", program.getIdentifierCount());
reportCountStatistic("Symbols", program.getSymbolCount());
reportCountStatistic("Types", program.getTypeCount());
reportCountStatistic("Instantiations", program.getInstantiationCount());
}
if (memoryUsed >= 0) {
reportStatisticalValue({ name: "Memory used", value: memoryUsed, type: StatisticType.memory }, /*aggregate*/ true);
}
@ -1073,11 +1090,13 @@ namespace ts {
const checkTime = isPerformanceEnabled ? performance.getDuration("Check") : 0;
const emitTime = isPerformanceEnabled ? performance.getDuration("Emit") : 0;
if (compilerOptions.extendedDiagnostics) {
const caches = program.getRelationCacheSizes();
reportCountStatistic("Assignability cache size", caches.assignable);
reportCountStatistic("Identity cache size", caches.identity);
reportCountStatistic("Subtype cache size", caches.subtype);
reportCountStatistic("Strict subtype cache size", caches.strictSubtype);
if (program) {
const caches = program.getRelationCacheSizes();
reportCountStatistic("Assignability cache size", caches.assignable);
reportCountStatistic("Identity cache size", caches.identity);
reportCountStatistic("Subtype cache size", caches.subtype);
reportCountStatistic("Strict subtype cache size", caches.strictSubtype);
}
if (isPerformanceEnabled) {
performance.forEachMeasure((name, duration) => {
if (!isSolutionMarkOrMeasure(name)) reportTimeStatistic(`${name} time`, duration, /*aggregate*/ true);

View File

@ -4578,8 +4578,8 @@ declare namespace ts {
function createUnparsedSourceFile(inputFile: InputFiles, type: "js" | "dts", stripInternal?: boolean): UnparsedSource;
function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
function createInputFiles(javascriptText: string, declarationText: string): InputFiles;
function createInputFiles(readFileText: (path: string) => string | undefined, javascriptPath: string, javascriptMapPath: string | undefined, declarationPath: string, declarationMapPath: string | undefined, buildInfoPath: string | undefined): InputFiles;
function createInputFiles(javascriptText: string, declarationText: string, javascriptMapPath: string | undefined, javascriptMapText: string | undefined, declarationMapPath: string | undefined, declarationMapText: string | undefined): InputFiles;
function createInputFiles(readFileText: (path: string) => string | undefined, javascriptPath: string, javascriptMapPath: string | undefined, declarationPath: string, declarationMapPath: string | undefined, buildInfoPath: string | undefined): InputFiles;
/**
* Create an external source map source file reference
*/

View File

@ -4578,8 +4578,8 @@ declare namespace ts {
function createUnparsedSourceFile(inputFile: InputFiles, type: "js" | "dts", stripInternal?: boolean): UnparsedSource;
function createUnparsedSourceFile(text: string, mapPath: string | undefined, map: string | undefined): UnparsedSource;
function createInputFiles(javascriptText: string, declarationText: string): InputFiles;
function createInputFiles(readFileText: (path: string) => string | undefined, javascriptPath: string, javascriptMapPath: string | undefined, declarationPath: string, declarationMapPath: string | undefined, buildInfoPath: string | undefined): InputFiles;
function createInputFiles(javascriptText: string, declarationText: string, javascriptMapPath: string | undefined, javascriptMapText: string | undefined, declarationMapPath: string | undefined, declarationMapText: string | undefined): InputFiles;
function createInputFiles(readFileText: (path: string) => string | undefined, javascriptPath: string, javascriptMapPath: string | undefined, declarationPath: string, declarationMapPath: string | undefined, buildInfoPath: string | undefined): InputFiles;
/**
* Create an external source map source file reference
*/

View File

@ -161,8 +161,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -1984,7 +1982,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -3339,7 +3337,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -167,8 +167,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -2765,7 +2763,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -4723,7 +4721,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -6257,7 +6255,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -166,8 +166,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -2298,7 +2296,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -3853,7 +3851,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -5060,7 +5058,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -165,8 +165,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
"/src/2/second-output.js.map": 1,
@ -2013,7 +2011,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -3387,7 +3385,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -161,8 +161,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -2074,7 +2072,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -3489,7 +3487,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -4562,7 +4560,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -186,13 +186,11 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/second/second_part1.ts": 1,
"/src/second/second_part2.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
"/src/first/bin/first-output.d.ts.map": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/2/second-output.js": 1,
"/src/2/second-output.js.map": 1,
"/src/2/second-output.d.ts.map": 1
@ -5796,11 +5794,11 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
"/src/2/second-output.tsbuildinfo": 2,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.d.ts": 1,
"/src/second/second_part1.ts": 1,
"/src/second/second_part2.ts": 1,
@ -11413,11 +11411,11 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
"/src/2/second-output.tsbuildinfo": 2,
"/src/2/second-output.tsbuildinfo": 1,
"/src/2/second-output.js": 2,
"/src/2/second-output.js.map": 2,
"/src/2/second-output.d.ts": 2,
@ -15816,11 +15814,11 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
"/src/2/second-output.tsbuildinfo": 2,
"/src/2/second-output.tsbuildinfo": 1,
"/src/2/second-output.js": 2,
"/src/2/second-output.js.map": 2,
"/src/2/second-output.d.ts": 2,

View File

@ -187,8 +187,6 @@ readFiles:: {
"/src/first/bin/first-output.d.ts": 1,
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -5503,7 +5501,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -8254,7 +8252,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,
@ -10640,7 +10638,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/first_part3.ts": 1,

View File

@ -179,8 +179,6 @@ readFiles:: {
"/src/2/second-output.d.ts": 1,
"/src/third/third_part1.ts": 1,
"/src/third/tripleRef.d.ts": 1,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/2/second-output.tsbuildinfo": 1,
"/src/first/bin/first-output.js": 1,
"/src/2/second-output.js": 1,
"/src/first/bin/first-output.js.map": 1,
@ -2336,7 +2334,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/tripleRef.d.ts": 1,
@ -3961,7 +3959,7 @@ readFiles:: {
"/src/third/tsconfig.json": 1,
"/src/first/tsconfig.json": 1,
"/src/second/tsconfig.json": 1,
"/src/first/bin/first-output.tsbuildinfo": 2,
"/src/first/bin/first-output.tsbuildinfo": 1,
"/src/first/first_PART1.ts": 1,
"/src/first/first_part2.ts": 1,
"/src/first/tripleRef.d.ts": 1,