@@ -133,85 +133,104 @@ function createLanguageServiceVfs(): vfs.FileSystem {
133133 return vfs . createFourslashVfs ( IO , /*ignoreCase*/ true , { cwd : virtualFileSystemRoot } ) ;
134134}
135135
136- const sharedLibDocumentRegistry = ts . createDocumentRegistry ( /*useCaseSensitiveFileNames*/ false , virtualFileSystemRoot ) ;
136+ // Cache lib file SourceFiles directly to avoid reparsing across tests.
137+ // Lib .d.ts files parse identically regardless of compiler settings, so we cache
138+ // just one copy per path. This persists for the entire test run.
139+ const libSourceFileCache = new Map < string , ts . SourceFile > ( ) ;
137140
138- function createLibSharingDocumentRegistry ( ) : ts . DocumentRegistry {
141+ function createLibCachingDocumentRegistry ( ) : ts . DocumentRegistry {
139142 const localRegistry = ts . createDocumentRegistry ( /*useCaseSensitiveFileNames*/ false , virtualFileSystemRoot ) ;
140143
141- const enableLibAstCaching = true ;
142- if ( ! enableLibAstCaching ) {
143- return localRegistry ;
144- }
145-
146- const libFileVersions = new Map < string , string > ( ) ;
147-
148144 function isLibFile ( fileName : string ) : boolean {
149145 return vpath . beneath ( vfs . fourslashLibFolder , fileName ) ;
150146 }
151147
152- function getRegistry ( fileName : string ) : ts . DocumentRegistry {
153- return isLibFile ( fileName ) ? sharedLibDocumentRegistry : localRegistry ;
154- }
155-
156- function checkLibFileVersion ( fileName : string , version : string ) : void {
157- if ( ! isLibFile ( fileName ) ) return ;
158- const existingVersion = libFileVersions . get ( fileName ) ;
159- if ( existingVersion === undefined ) {
160- libFileVersions . set ( fileName , version ) ;
161- }
162- else if ( existingVersion !== version ) {
163- throw new Error ( `Lib file "${ fileName } " version changed from "${ existingVersion } " to "${ version } " - lib files should not be modified` ) ;
164- }
165- }
166-
167148 return {
168149 acquireDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) {
169- checkLibFileVersion ( fileName , version ) ;
170- return getRegistry ( fileName ) . acquireDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
150+ if ( isLibFile ( fileName ) ) {
151+ const cached = libSourceFileCache . get ( fileName ) ;
152+ if ( cached ) {
153+ return cached ;
154+ }
155+ // Not cached - acquire from local registry to parse it, then cache
156+ const sourceFile = localRegistry . acquireDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
157+ libSourceFileCache . set ( fileName , sourceFile ) ;
158+ // Release from local registry immediately - we don't need ref counting for cached libs
159+ const settings = typeof ( compilationSettingsOrHost as ts . MinimalResolutionCacheHost ) . getCompilationSettings === "function"
160+ ? ( compilationSettingsOrHost as ts . MinimalResolutionCacheHost ) . getCompilationSettings ( )
161+ : compilationSettingsOrHost as ts . CompilerOptions ;
162+ localRegistry . releaseDocument ( fileName , settings , scriptKind ! , typeof sourceFileOptions === "object" ? sourceFileOptions . impliedNodeFormat : undefined ) ;
163+ return sourceFile ;
164+ }
165+ return localRegistry . acquireDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
171166 } ,
172167 acquireDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) {
173- checkLibFileVersion ( fileName , version ) ;
174- return getRegistry ( fileName ) . acquireDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
168+ if ( isLibFile ( fileName ) ) {
169+ const cached = libSourceFileCache . get ( fileName ) ;
170+ if ( cached ) {
171+ return cached ;
172+ }
173+ // Not cached - acquire from local registry to parse it, then cache
174+ const sourceFile = localRegistry . acquireDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
175+ libSourceFileCache . set ( fileName , sourceFile ) ;
176+ // Release from local registry immediately
177+ const impliedNodeFormat = typeof sourceFileOptions === "object" ? sourceFileOptions . impliedNodeFormat : undefined ;
178+ localRegistry . releaseDocumentWithKey ( path , key , scriptKind ! , impliedNodeFormat ) ;
179+ return sourceFile ;
180+ }
181+ return localRegistry . acquireDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
175182 } ,
176183 updateDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) {
177- checkLibFileVersion ( fileName , version ) ;
178- return getRegistry ( fileName ) . updateDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
184+ if ( isLibFile ( fileName ) ) {
185+ // Lib files should never be updated - just return the cached version
186+ const cached = libSourceFileCache . get ( fileName ) ;
187+ if ( cached ) {
188+ return cached ;
189+ }
190+ // Fall through to acquire if somehow not cached
191+ return this . acquireDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
192+ }
193+ return localRegistry . updateDocument ( fileName , compilationSettingsOrHost , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
179194 } ,
180195 updateDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) {
181- checkLibFileVersion ( fileName , version ) ;
182- return getRegistry ( fileName ) . updateDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
196+ if ( isLibFile ( fileName ) ) {
197+ // Lib files should never be updated - just return the cached version
198+ const cached = libSourceFileCache . get ( fileName ) ;
199+ if ( cached ) {
200+ return cached ;
201+ }
202+ // Fall through to acquire if somehow not cached
203+ return this . acquireDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
204+ }
205+ return localRegistry . updateDocumentWithKey ( fileName , path , compilationSettingsOrHost , key , scriptSnapshot , version , scriptKind , sourceFileOptions ) ;
183206 } ,
184207 getKeyForCompilationSettings ( settings ) {
185- return sharedLibDocumentRegistry . getKeyForCompilationSettings ( settings ) ;
208+ return localRegistry . getKeyForCompilationSettings ( settings ) ;
186209 } ,
187210 getDocumentRegistryBucketKeyWithMode ( key , mode ) {
188- return sharedLibDocumentRegistry . getDocumentRegistryBucketKeyWithMode ( key , mode ) ;
211+ return localRegistry . getDocumentRegistryBucketKeyWithMode ( key , mode ) ;
189212 } ,
190213 releaseDocument ( fileName : string , compilationSettings : ts . CompilerOptions , scriptKind ?: ts . ScriptKind , impliedNodeFormat ?: ts . ResolutionMode ) {
191- // Need to handle the overloaded signature
192- return getRegistry ( fileName ) . releaseDocument ( fileName , compilationSettings , scriptKind ! , impliedNodeFormat ) ;
214+ if ( isLibFile ( fileName ) ) {
215+ // Lib files are cached separately - no ref counting needed, so no-op
216+ return ;
217+ }
218+ return localRegistry . releaseDocument ( fileName , compilationSettings , scriptKind ! , impliedNodeFormat ) ;
193219 } ,
194220 releaseDocumentWithKey ( path : ts . Path , key : ts . DocumentRegistryBucketKey , scriptKind ?: ts . ScriptKind , impliedNodeFormat ?: ts . ResolutionMode ) {
195- // We don 't know the original file name here, so release from both registries
196- // This is safe because releaseDocument is a no-op if the document wasn't acquired
221+ // We can 't easily tell if this is a lib file from just the path without the fileName,
222+ // so try to release from local registry and ignore errors
197223 try {
198224 localRegistry . releaseDocumentWithKey ( path , key , scriptKind ! , impliedNodeFormat ) ;
199225 }
200226 catch {
201- // Ignore - document might be in the other registry
202- }
203- try {
204- sharedLibDocumentRegistry . releaseDocumentWithKey ( path , key , scriptKind ! , impliedNodeFormat ) ;
205- }
206- catch {
207- // Ignore - document might be in the other registry
227+ // Ignore - might be a lib file that was never in the local registry
208228 }
209229 } ,
210230 reportStats ( ) {
211- return `Shared lib registry : ${ sharedLibDocumentRegistry . reportStats ( ) } \nLocal registry: ${ localRegistry . reportStats ( ) } ` ;
231+ return `Lib cache : ${ libSourceFileCache . size } files \nLocal registry: ${ localRegistry . reportStats ( ) } ` ;
212232 } ,
213233 getBuckets ( ) {
214- // Return local buckets - this is primarily for debugging
215234 return localRegistry . getBuckets ( ) ;
216235 } ,
217236 } ;
@@ -429,7 +448,7 @@ export class NativeLanguageServiceAdapter implements LanguageServiceAdapter {
429448 getLogger : typeof ts . returnUndefined = ts . returnUndefined ;
430449 constructor ( cancellationToken ?: ts . HostCancellationToken , options ?: ts . CompilerOptions ) {
431450 this . host = new NativeLanguageServiceHost ( cancellationToken , options ) ;
432- this . documentRegistry = createLibSharingDocumentRegistry ( ) ;
451+ this . documentRegistry = createLibCachingDocumentRegistry ( ) ;
433452 }
434453 getHost ( ) : LanguageServiceAdapterHost {
435454 return this . host ;
0 commit comments