diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 23d7b76a0ea..d01b2bd05b7 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -170,6 +170,11 @@ export interface ReusableBuilderProgramState extends BuilderState { modules: Map> | undefined; typeRefs: Map> | undefined; }; + resuableCacheResolutions?: { + resolutions: ProgramBuildInfoCacheResolutions; + fileNames: readonly string[]; + buildInfoPath: string; + } } /** @internal */ @@ -496,6 +501,8 @@ function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRel */ function releaseCache(state: BuilderProgramState) { BuilderState.releaseCache(state); + // Store the resolutions if possible + if (!state.resuableCacheResolutions) getCacheResolutions(state); state.program = undefined; } @@ -1013,9 +1020,10 @@ export function isProgramBundleEmitBuildInfo(info: ProgramBuildInfo): info is Pr /** * 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 getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | undefined, buildInfoPath: string,): BuildInfo { const currentDirectory = Debug.checkDefined(state.program).getCurrentDirectory(); - const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(getTsBuildInfoEmitOutputFilePath(state.compilerOptions)!, currentDirectory)); + buildInfoPath = getNormalizedAbsolutePath(buildInfoPath, currentDirectory); + const buildInfoDirectory = getDirectoryPath(buildInfoPath); // 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 const latestChangedDtsFile = state.latestChangedDtsFile ? relativeToBuildInfoEnsuringAbsolutePath(state.latestChangedDtsFile) : undefined; const fileNames: string[] = []; @@ -1271,13 +1279,18 @@ function getBuildInfo(state: BuilderProgramState, bundle: BundleBuildInfo | unde if (!resolutions) return; Debug.assertIsDefined(names); Debug.assertIsDefined(resolutionEntries); - return { - resolutions, - names, - resolutionEntries, - modules, - typeRefs, + state.resuableCacheResolutions = { + resolutions: { + resolutions, + names, + resolutionEntries, + modules, + typeRefs, + }, + fileNames, + buildInfoPath, }; + return state.resuableCacheResolutions.resolutions; } function toProgramBuildInfoResolutionCache( @@ -1495,7 +1508,7 @@ export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, hos } const state = createBuilderProgramState(newProgram, oldState); - newProgram.getBuildInfo = bundle => getBuildInfo(state, bundle); + newProgram.getBuildInfo = (bundle, buildInfoPath) => getBuildInfo(state, bundle, buildInfoPath); // To ensure that we arent storing any references to old program or new program without state newProgram = undefined!; // TODO: GH#18217 @@ -1839,14 +1852,38 @@ export function toProgramEmitPending(value: ProgramBuildInfoBundlePendingEmit, o return !value ? getBuilderFileEmit(options || {}) : value; } -/** @internal */ -export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram { - const program = buildInfo.program!; +function getProgramBuildInfoFilePathDecoder(fileNames: readonly string[], buildInfoPath: string, host: ReadBuildProgramHost) { const buildInfoDirectory = getDirectoryPath(getNormalizedAbsolutePath(buildInfoPath, host.getCurrentDirectory())); const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); + let filePaths: Path[] | undefined; + let fileAbsolutePaths: string[] | undefined; + return { toAbsolutePath, toFilePath, toFileAbsolutePath }; + + function toPath(path: string) { + return ts.toPath(path, buildInfoDirectory, getCanonicalFileName); + } + + function toAbsolutePath(path: string) { + return getNormalizedAbsolutePath(path, buildInfoDirectory); + } + + function toFilePath(fileId: ProgramBuildInfoFileId): Path { + return filePaths?.[fileId - 1] ?? + ((filePaths ??= new Array(fileNames.length))[fileId - 1] = toPath(fileNames[fileId - 1])); + } + + function toFileAbsolutePath(fileId: ProgramBuildInfoAbsoluteFileId): string { + return fileAbsolutePaths?.[fileId - 1] ?? + ((fileAbsolutePaths ??= new Array(fileNames.length))[fileId - 1] = toAbsolutePath(fileNames[fileId - 1])); + } +} + +/** @internal */ +export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, buildInfoPath: string, host: ReadBuildProgramHost): EmitAndSemanticDiagnosticsBuilderProgram { + const program = buildInfo.program!; + const { toFilePath, toAbsolutePath } = getProgramBuildInfoFilePathDecoder(program.fileNames, buildInfoPath, host); let state: ReusableBuilderProgramState; - const filePaths = program.fileNames?.map(toPath); let filePathsSetList: Set[] | undefined; const latestChangedDtsFile = program.latestChangedDtsFile ? toAbsolutePath(program.latestChangedDtsFile) : undefined; if (isProgramBundleEmitBuildInfo(program)) { @@ -1862,6 +1899,7 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, outSignature: program.outSignature, programEmitPending: program.pendingEmit === undefined ? undefined : toProgramEmitPending(program.pendingEmit, program.options), bundle: buildInfo.bundle, + resuableCacheResolutions: toResuableCacheResolutions(), }; } else { @@ -1897,6 +1935,7 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, changedFilesSet: new Set(map(program.changeFileSet, toFilePath)), latestChangedDtsFile, emitSignatures: emitSignatures?.size ? emitSignatures : undefined, + resuableCacheResolutions: toResuableCacheResolutions(), }; } @@ -1926,18 +1965,6 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, hasChangedEmitSignature: returnFalse, }; - function toPath(path: string) { - return ts.toPath(path, buildInfoDirectory, getCanonicalFileName); - } - - function toAbsolutePath(path: string) { - return getNormalizedAbsolutePath(path, buildInfoDirectory); - } - - function toFilePath(fileId: ProgramBuildInfoFileId) { - return filePaths[fileId - 1]; - } - function toFilePathsSet(fileIdsListId: ProgramBuildInfoFileIdListId) { return filePathsSetList![fileIdsListId - 1]; } @@ -1953,6 +1980,14 @@ export function createBuilderProgramUsingProgramBuildInfo(buildInfo: BuildInfo, ); return map; } + + function toResuableCacheResolutions(): BuilderProgramState["resuableCacheResolutions"] { + return program.cacheResolutions && { + resolutions: program.cacheResolutions, + fileNames: program.fileNames, + buildInfoPath, + }; + } } /** @internal */ diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d6da4d7d1f4..7b6d0a2371a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -811,7 +811,7 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi emitSkipped = true; return; } - const buildInfo = host.getBuildInfo(bundle) || createBuildInfo(/*program*/ undefined, bundle); + const buildInfo = host.getBuildInfo(bundle, buildInfoPath) || createBuildInfo(/*program*/ undefined, bundle); // Pass buildinfo as additional data to avoid having to reparse writeFile(host, emitterDiagnostics, buildInfoPath, getBuildInfoText(buildInfo), /*writeByteOrderMark*/ false, /*sourceFiles*/ undefined, { buildInfo }); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7da8c4ea09d..705efab4f8a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2491,7 +2491,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return host.fileExists(f); }, useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), - getBuildInfo: bundle => program.getBuildInfo?.(bundle), + getBuildInfo: (bundle, buildInfoPath) => program.getBuildInfo?.(bundle, buildInfoPath), getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref), redirectTargetsMap, getFileIncludeReasons: program.getFileIncludeReasons, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c56ba2dd688..e748999faa8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4539,7 +4539,7 @@ export interface Program extends ScriptReferenceHost { /** @internal */ forEachResolvedProjectReference(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; /** @internal */ getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined; /** @internal */ isSourceOfProjectReferenceRedirect(fileName: string): boolean; - /** @internal */ getBuildInfo?(bundle: BundleBuildInfo | undefined): BuildInfo; + /** @internal */ getBuildInfo?(bundle: BundleBuildInfo | undefined, buildInfoPath: string): BuildInfo; /** @internal */ emitBuildInfo(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult; /** * This implementation handles file exists to be true if file is source of project reference redirect when program is created using useSourceOfProjectReferenceRedirect @@ -7687,7 +7687,7 @@ export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolution getPrependNodes(): readonly (InputFiles | UnparsedSource)[]; writeFile: WriteFileCallback; - getBuildInfo(bundle: BundleBuildInfo | undefined): BuildInfo | undefined; + getBuildInfo(bundle: BundleBuildInfo | undefined, buildInfoPath: string): BuildInfo | undefined; getSourceFileFromReference: Program["getSourceFileFromReference"]; readonly redirectTargetsMap: RedirectTargetsMap; createHash?(data: string): string;