diff --git a/packages/typert/generator/src/analyzer.ts b/packages/typert/generator/src/analyzer.ts index 1f545d9673..e0e04a4fa1 100644 --- a/packages/typert/generator/src/analyzer.ts +++ b/packages/typert/generator/src/analyzer.ts @@ -131,6 +131,26 @@ interface FaceProgramHost { readonly files: Map } +/** + * Process-wide parse cache for the bundled TypeScript default libraries. + * `typescript/lib/lib.*.d.ts` content is immutable for the process lifetime, + * so parses are shared across every {@link WorkspaceCaches} instance; the key + * carries the parse-affecting settings, keeping reuse exact. + */ +const defaultLibraryParses = new Map() + +function defaultLibraryKey(fileName: string, languageVersionOrOptions: ts.ScriptTarget | ts.CreateSourceFileOptions): string { + const options = typeof languageVersionOrOptions === 'object' + ? languageVersionOrOptions + : { languageVersion: languageVersionOrOptions } + return [ + fileName, + String(options.languageVersion), + String(options.impliedNodeFormat ?? ''), + String(options.jsDocParsingMode ?? ''), + ].join('\0') +} + /** * Shared memo over one immutable workspace snapshot. Passing one instance to * several analyzers (the batched and write-mode children reuse their parent's @@ -185,6 +205,13 @@ export class WorkspaceCaches { // only fires under oldProgram reuse, which these fresh programs never // request, and invalidate() is the one supported re-read path. host.getSourceFile = (fileName, languageVersionOrOptions, onError) => { + if (isStandardLibraryFile(fileName)) { + const key = defaultLibraryKey(fileName, languageVersionOrOptions) + if (!defaultLibraryParses.has(key)) { + defaultLibraryParses.set(key, base(fileName, languageVersionOrOptions, onError)) + } + return defaultLibraryParses.get(key) + } if (!files.has(fileName)) files.set(fileName, base(fileName, languageVersionOrOptions, onError)) return files.get(fileName) }