Skip to content

Commit 354891c

Browse files
authored
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
1 parent f0216e3 commit 354891c

17 files changed

+262
-237
lines changed

src/compiler/emitter.ts

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -705,17 +705,7 @@ namespace ts {
705705
/** File that isnt present resulting in error or output files */
706706
export type EmitUsingBuildInfoResult = string | readonly OutputFile[];
707707

708-
/*@internal*/
709-
export interface EmitUsingBuildInfoHost extends ModuleResolutionHost {
710-
getCurrentDirectory(): string;
711-
getCanonicalFileName(fileName: string): string;
712-
useCaseSensitiveFileNames(): boolean;
713-
getNewLine(): string;
714-
createHash?(data: string): string;
715-
getBuildInfo?(fileName: string, configFilePath: string | undefined): BuildInfo | undefined;
716-
}
717-
718-
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: EmitUsingBuildInfoHost): readonly SourceFile[] {
708+
function createSourceFilesFromBundleBuildInfo(bundle: BundleBuildInfo, buildInfoDirectory: string, host: CompilerHost): readonly SourceFile[] {
719709
const jsBundle = Debug.checkDefined(bundle.js);
720710
const prologueMap = jsBundle.sources?.prologues && arrayToMap(jsBundle.sources.prologues, prologueInfo => prologueInfo.file);
721711
return bundle.sourceFiles.map((fileName, index) => {
@@ -745,22 +735,29 @@ namespace ts {
745735
/*@internal*/
746736
export function emitUsingBuildInfo(
747737
config: ParsedCommandLine,
748-
host: EmitUsingBuildInfoHost,
738+
host: CompilerHost,
739+
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
740+
customTransformers?: CustomTransformers
741+
): EmitUsingBuildInfoResult {
742+
tracing?.push(tracing.Phase.Emit, "emitUsingBuildInfo", {}, /*separateBeginAndEnd*/ true);
743+
performance.mark("beforeEmit");
744+
const result = emitUsingBuildInfoWorker(config, host, getCommandLine, customTransformers);
745+
performance.mark("afterEmit");
746+
performance.measure("Emit", "beforeEmit", "afterEmit");
747+
tracing?.pop();
748+
return result;
749+
}
750+
751+
function emitUsingBuildInfoWorker(
752+
config: ParsedCommandLine,
753+
host: CompilerHost,
749754
getCommandLine: (ref: ProjectReference) => ParsedCommandLine | undefined,
750755
customTransformers?: CustomTransformers
751756
): EmitUsingBuildInfoResult {
752757
const createHash = maybeBind(host, host.createHash);
753758
const { buildInfoPath, jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath } = getOutputPathsForBundle(config.options, /*forceDtsPaths*/ false);
754-
let buildInfo: BuildInfo | undefined;
755-
if (host.getBuildInfo) {
756-
// If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo
757-
buildInfo = host.getBuildInfo(buildInfoPath!, config.options.configFilePath);
758-
}
759-
else {
760-
const buildInfoText = host.readFile(buildInfoPath!);
761-
if (!buildInfoText) return buildInfoPath!;
762-
buildInfo = getBuildInfo(buildInfoPath!, buildInfoText);
763-
}
759+
// If host directly provides buildinfo we can get it directly. This allows host to cache the buildinfo
760+
const buildInfo = host.getBuildInfo!(buildInfoPath!, config.options.configFilePath);
764761
if (!buildInfo) return buildInfoPath!;
765762
if (!buildInfo.bundle || !buildInfo.bundle.js || (declarationFilePath && !buildInfo.bundle.dts)) return buildInfoPath!;
766763

@@ -783,28 +780,28 @@ namespace ts {
783780
if (declarationMapPath && computeSignature(declarationMapText!, createHash) !== buildInfo.bundle.dts!.mapHash) return declarationMapPath;
784781

785782
const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath!, host.getCurrentDirectory()));
786-
const ownPrependInput = createInputFiles(
783+
const ownPrependInput = createInputFilesWithFileTexts(
784+
jsFilePath,
787785
jsFileText,
788-
declarationText!,
789786
sourceMapFilePath,
790787
sourceMapText,
788+
declarationFilePath,
789+
declarationText!,
791790
declarationMapPath,
792791
declarationMapText,
793-
jsFilePath,
794-
declarationFilePath,
795792
buildInfoPath,
796793
buildInfo,
797794
/*onlyOwnText*/ true
798795
);
799796
const outputFiles: OutputFile[] = [];
800-
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f));
797+
const prependNodes = createPrependNodes(config.projectReferences, getCommandLine, f => host.readFile(f), host);
801798
const sourceFilesForJsEmit = createSourceFilesFromBundleBuildInfo(buildInfo.bundle, buildInfoDirectory, host);
802799
let changedDtsText: string | undefined;
803800
let changedDtsData: WriteFileCallbackData | undefined;
804801
const emitHost: EmitHost = {
805802
getPrependNodes: memoize(() => [...prependNodes, ownPrependInput]),
806803
getCanonicalFileName: host.getCanonicalFileName,
807-
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo!.bundle!.commonSourceDirectory, buildInfoDirectory),
804+
getCommonSourceDirectory: () => getNormalizedAbsolutePath(buildInfo.bundle!.commonSourceDirectory, buildInfoDirectory),
808805
getCompilerOptions: () => config.options,
809806
getCurrentDirectory: () => host.getCurrentDirectory(),
810807
getNewLine: () => host.getNewLine(),
@@ -826,13 +823,13 @@ namespace ts {
826823
break;
827824
case buildInfoPath:
828825
const newBuildInfo = data!.buildInfo!;
829-
newBuildInfo.program = buildInfo!.program;
826+
newBuildInfo.program = buildInfo.program;
830827
if (newBuildInfo.program && changedDtsText !== undefined && config.options.composite) {
831828
// Update the output signature
832829
(newBuildInfo.program as ProgramBundleEmitBuildInfo).outSignature = computeSignature(changedDtsText, createHash, changedDtsData);
833830
}
834831
// Update sourceFileInfo
835-
const { js, dts, sourceFiles } = buildInfo!.bundle!;
832+
const { js, dts, sourceFiles } = buildInfo.bundle!;
836833
newBuildInfo.bundle!.js!.sources = js!.sources;
837834
if (dts) {
838835
newBuildInfo.bundle!.dts!.sources = dts.sources;

src/compiler/factory/nodeFactory.ts

Lines changed: 100 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -6742,14 +6742,6 @@ namespace ts {
67426742
javascriptText: string,
67436743
declarationText: string
67446744
): InputFiles;
6745-
export function createInputFiles(
6746-
readFileText: (path: string) => string | undefined,
6747-
javascriptPath: string,
6748-
javascriptMapPath: string | undefined,
6749-
declarationPath: string,
6750-
declarationMapPath: string | undefined,
6751-
buildInfoPath: string | undefined
6752-
): InputFiles;
67536745
export function createInputFiles(
67546746
javascriptText: string,
67556747
declarationText: string,
@@ -6758,19 +6750,13 @@ namespace ts {
67586750
declarationMapPath: string | undefined,
67596751
declarationMapText: string | undefined
67606752
): InputFiles;
6761-
/*@internal*/
67626753
export function createInputFiles(
6763-
javascriptText: string,
6764-
declarationText: string,
6754+
readFileText: (path: string) => string | undefined,
6755+
javascriptPath: string,
67656756
javascriptMapPath: string | undefined,
6766-
javascriptMapText: string | undefined,
6757+
declarationPath: string,
67676758
declarationMapPath: string | undefined,
6768-
declarationMapText: string | undefined,
6769-
javascriptPath: string | undefined,
6770-
declarationPath: string | undefined,
6771-
buildInfoPath?: string | undefined,
6772-
buildInfo?: BuildInfo,
6773-
oldFileOfCurrentEmit?: boolean
6759+
buildInfoPath: string | undefined
67746760
): InputFiles;
67756761
export function createInputFiles(
67766762
javascriptTextOrReadFileText: string | ((path: string) => string | undefined),
@@ -6779,62 +6765,106 @@ namespace ts {
67796765
javascriptMapTextOrDeclarationPath?: string,
67806766
declarationMapPath?: string,
67816767
declarationMapTextOrBuildInfoPath?: string,
6782-
javascriptPath?: string | undefined,
6783-
declarationPath?: string | undefined,
6784-
buildInfoPath?: string | undefined,
6785-
buildInfo?: BuildInfo,
6786-
oldFileOfCurrentEmit?: boolean
6768+
): InputFiles {
6769+
return !isString(javascriptTextOrReadFileText) ?
6770+
createInputFilesWithFilePaths(
6771+
javascriptTextOrReadFileText,
6772+
declarationTextOrJavascriptPath,
6773+
javascriptMapPath,
6774+
javascriptMapTextOrDeclarationPath!,
6775+
declarationMapPath,
6776+
declarationMapTextOrBuildInfoPath,
6777+
) :
6778+
createInputFilesWithFileTexts(
6779+
/*javascriptPath*/ undefined,
6780+
javascriptTextOrReadFileText,
6781+
javascriptMapPath,
6782+
javascriptMapTextOrDeclarationPath,
6783+
/*declarationPath*/ undefined,
6784+
declarationTextOrJavascriptPath,
6785+
declarationMapPath,
6786+
declarationMapTextOrBuildInfoPath,
6787+
);
6788+
}
6789+
/*@internal*/
6790+
export function createInputFilesWithFilePaths(
6791+
readFileText: (path: string) => string | undefined,
6792+
javascriptPath: string,
6793+
javascriptMapPath: string | undefined,
6794+
declarationPath: string,
6795+
declarationMapPath: string | undefined,
6796+
buildInfoPath: string | undefined,
6797+
host?: CompilerHost,
6798+
options?: CompilerOptions,
67876799
): InputFiles {
67886800
const node = parseNodeFactory.createInputFiles();
6789-
if (!isString(javascriptTextOrReadFileText)) {
6790-
const cache = new Map<string, string | false>();
6791-
const textGetter = (path: string | undefined) => {
6792-
if (path === undefined) return undefined;
6793-
let value = cache.get(path);
6794-
if (value === undefined) {
6795-
value = javascriptTextOrReadFileText(path);
6796-
cache.set(path, value !== undefined ? value : false);
6801+
node.javascriptPath = javascriptPath;
6802+
node.javascriptMapPath = javascriptMapPath;
6803+
node.declarationPath = declarationPath;
6804+
node.declarationMapPath = declarationMapPath;
6805+
node.buildInfoPath = buildInfoPath;
6806+
const cache = new Map<string, string | false>();
6807+
const textGetter = (path: string | undefined) => {
6808+
if (path === undefined) return undefined;
6809+
let value = cache.get(path);
6810+
if (value === undefined) {
6811+
value = readFileText(path);
6812+
cache.set(path, value !== undefined ? value : false);
6813+
}
6814+
return value !== false ? value as string : undefined;
6815+
};
6816+
const definedTextGetter = (path: string) => {
6817+
const result = textGetter(path);
6818+
return result !== undefined ? result : `/* Input file ${path} was missing */\r\n`;
6819+
};
6820+
let buildInfo: BuildInfo | false;
6821+
const getAndCacheBuildInfo = () => {
6822+
if (buildInfo === undefined && buildInfoPath) {
6823+
if (host?.getBuildInfo) {
6824+
buildInfo = host.getBuildInfo(buildInfoPath, options!.configFilePath) ?? false;
67976825
}
6798-
return value !== false ? value as string : undefined;
6799-
};
6800-
const definedTextGetter = (path: string) => {
6801-
const result = textGetter(path);
6802-
return result !== undefined ? result : `/* Input file ${path} was missing */\r\n`;
6803-
};
6804-
let buildInfo: BuildInfo | false;
6805-
const getAndCacheBuildInfo = (getText: () => string | undefined) => {
6806-
if (buildInfo === undefined) {
6807-
const result = getText();
6808-
buildInfo = result !== undefined ? getBuildInfo(node.buildInfoPath!, result) ?? false : false;
6826+
else {
6827+
const result = textGetter(buildInfoPath);
6828+
buildInfo = result !== undefined ? getBuildInfo(buildInfoPath, result) ?? false : false;
68096829
}
6810-
return buildInfo || undefined;
6811-
};
6812-
node.javascriptPath = declarationTextOrJavascriptPath;
6813-
node.javascriptMapPath = javascriptMapPath;
6814-
node.declarationPath = Debug.checkDefined(javascriptMapTextOrDeclarationPath);
6815-
node.declarationMapPath = declarationMapPath;
6816-
node.buildInfoPath = declarationMapTextOrBuildInfoPath;
6817-
Object.defineProperties(node, {
6818-
javascriptText: { get() { return definedTextGetter(declarationTextOrJavascriptPath); } },
6819-
javascriptMapText: { get() { return textGetter(javascriptMapPath); } }, // TODO:: if there is inline sourceMap in jsFile, use that
6820-
declarationText: { get() { return definedTextGetter(Debug.checkDefined(javascriptMapTextOrDeclarationPath)); } },
6821-
declarationMapText: { get() { return textGetter(declarationMapPath); } }, // TODO:: if there is inline sourceMap in dtsFile, use that
6822-
buildInfo: { get() { return getAndCacheBuildInfo(() => textGetter(declarationMapTextOrBuildInfoPath)); } }
6823-
});
6824-
}
6825-
else {
6826-
node.javascriptText = javascriptTextOrReadFileText;
6827-
node.javascriptMapPath = javascriptMapPath;
6828-
node.javascriptMapText = javascriptMapTextOrDeclarationPath;
6829-
node.declarationText = declarationTextOrJavascriptPath;
6830-
node.declarationMapPath = declarationMapPath;
6831-
node.declarationMapText = declarationMapTextOrBuildInfoPath;
6832-
node.javascriptPath = javascriptPath;
6833-
node.declarationPath = declarationPath;
6834-
node.buildInfoPath = buildInfoPath;
6835-
node.buildInfo = buildInfo;
6836-
node.oldFileOfCurrentEmit = oldFileOfCurrentEmit;
6837-
}
6830+
}
6831+
return buildInfo || undefined;
6832+
};
6833+
Object.defineProperties(node, {
6834+
javascriptText: { get: () => definedTextGetter(javascriptPath) },
6835+
javascriptMapText: { get: () => textGetter(javascriptMapPath) }, // TODO:: if there is inline sourceMap in jsFile, use that
6836+
declarationText: { get: () => definedTextGetter(Debug.checkDefined(declarationPath)) },
6837+
declarationMapText: { get: () => textGetter(declarationMapPath) }, // TODO:: if there is inline sourceMap in dtsFile, use that
6838+
buildInfo: { get: getAndCacheBuildInfo },
6839+
});
6840+
return node;
6841+
}
6842+
/*@internal*/
6843+
export function createInputFilesWithFileTexts(
6844+
javascriptPath: string | undefined,
6845+
javascriptText: string,
6846+
javascriptMapPath: string | undefined,
6847+
javascriptMapText: string | undefined,
6848+
declarationPath: string | undefined,
6849+
declarationText: string,
6850+
declarationMapPath: string | undefined,
6851+
declarationMapText: string | undefined,
6852+
buildInfoPath?: string,
6853+
buildInfo?: BuildInfo,
6854+
oldFileOfCurrentEmit?: boolean,
6855+
): InputFiles {
6856+
const node = parseNodeFactory.createInputFiles();
6857+
node.javascriptPath = javascriptPath;
6858+
node.javascriptText = javascriptText;
6859+
node.javascriptMapPath = javascriptMapPath;
6860+
node.javascriptMapText = javascriptMapText;
6861+
node.declarationPath = declarationPath;
6862+
node.declarationText = declarationText;
6863+
node.declarationMapPath = declarationMapPath;
6864+
node.declarationMapText = declarationMapText;
6865+
node.buildInfoPath = buildInfoPath;
6866+
node.buildInfo = buildInfo;
6867+
node.oldFileOfCurrentEmit = oldFileOfCurrentEmit;
68386868
return node;
68396869
}
68406870

0 commit comments

Comments
 (0)