@@ -232,6 +232,7 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
232232 private currentDirectory : string | undefined ;
233233 private getCanonicalFileNameWorker : ( ( fileName : string ) => string ) | undefined ;
234234 private initialized : boolean = false ;
235+ private initializing : Promise < void > | undefined ;
235236 private activeSnapshots : Set < Snapshot > = new Set ( ) ;
236237 private latestSnapshot : Snapshot | undefined ;
237238 readonly internal : InternalAPI ;
@@ -269,7 +270,12 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
269270 // @sync -only-end
270271
271272 private async ensureInitialized ( ) : Promise < void > {
272- if ( ! this . initialized ) {
273+ if ( this . initialized ) return ;
274+ return this . initializing ??= this . initializeWorker ( ) ;
275+ }
276+
277+ private async initializeWorker ( ) : Promise < void > {
278+ try {
273279 const response = await this . client . apiRequest ( "initialize" , null ) ;
274280 const getCanonicalFileName = createGetCanonicalFileName ( response . useCaseSensitiveFileNames ) ;
275281 const currentDirectory = response . currentDirectory ;
@@ -278,6 +284,10 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
278284 this . toPath = ( fileName : string ) => toPath ( fileName , currentDirectory , getCanonicalFileName ) as Path ;
279285 this . initialized = true ;
280286 }
287+ catch ( error ) {
288+ this . initializing = undefined ;
289+ throw error ;
290+ }
281291 }
282292
283293 getCurrentDirectory ( ) : string {
@@ -376,18 +386,27 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
376386 return snapshot ;
377387 }
378388
389+ async [ globalThis . Symbol . asyncDispose ] ( ) : Promise < void > { // @sync : [globalThis.Symbol.dispose](): void {
390+ await this . close ( ) ; // @sync : this.close();
391+ }
392+
379393 async close ( ) : Promise < void > {
394+ await this . initializing ?. catch ( ( ) => { } ) ; // @sync -skip
380395 // Dispose all active snapshots
381- for ( const snapshot of [ ...this . activeSnapshots ] ) {
382- await snapshot . dispose ( ) ;
396+ try {
397+ for ( const snapshot of [ ...this . activeSnapshots ] ) {
398+ await snapshot . dispose ( ) ;
399+ }
400+ // Release the latest snapshot's cache refs if still held
401+ if ( this . latestSnapshot ) {
402+ this . sourceFileCache . releaseSnapshot ( this . latestSnapshot . id ) ;
403+ this . latestSnapshot = undefined ;
404+ }
405+ this . sourceFileCache . clear ( ) ;
383406 }
384- // Release the latest snapshot's cache refs if still held
385- if ( this . latestSnapshot ) {
386- this . sourceFileCache . releaseSnapshot ( this . latestSnapshot . id ) ;
387- this . latestSnapshot = undefined ;
407+ finally {
408+ await this . client . close ( ) ; // always close the underlying connection
388409 }
389- await this . client . close ( ) ;
390- this . sourceFileCache . clear ( ) ;
391410 }
392411
393412 clearSourceFileCache ( ) : void {
@@ -539,6 +558,7 @@ export class Snapshot {
539558 private toPath : ( fileName : string ) => Path ;
540559 private client : Client ;
541560 private disposed : boolean = false ;
561+ private disposePromise : Promise < void > | undefined ;
542562 private onDispose : ( ) => void ;
543563 private snapshotRegistry : SnapshotObjectRegistry ;
544564 readonly internal : SnapshotInternalAPI ;
@@ -587,19 +607,27 @@ export class Snapshot {
587607 }
588608
589609 [ globalThis . Symbol . dispose ] ( ) : void {
590- this . dispose ( ) ;
610+ void this . dispose ( ) ;
591611 }
592612
593- async dispose ( ) : Promise < void > {
613+ dispose ( ) : Promise < void > {
614+ return this . disposePromise ??= this . disposeWorker ( ) ;
615+ }
616+
617+ private async disposeWorker ( ) : Promise < void > {
594618 if ( this . disposed ) return ;
595619 this . disposed = true ;
596620 for ( const project of this . projectMap . values ( ) ) {
597621 project . dispose ( ) ;
598622 }
599623 this . projectMap . clear ( ) ;
600624 this . snapshotRegistry . clear ( ) ;
601- this . onDispose ( ) ;
602- await this . client . apiRequest ( "release" , { snapshot : this . id } ) ;
625+ try {
626+ await this . client . apiRequest ( "release" , { snapshot : this . id } ) ;
627+ }
628+ finally {
629+ this . onDispose ( ) ;
630+ }
603631 }
604632
605633 isDisposed ( ) : boolean {
@@ -1082,6 +1110,7 @@ export class Program implements FormatDiagnosticsHost {
10821110 private readonly decoder = new Wtf8Decoder ( ) ;
10831111 private readonly sourceFileMetadataCache = new Map < Path , Promise < SourceFileMetadata | undefined > > ( ) ;
10841112 private ownedSnapshot : Snapshot | undefined ;
1113+ private disposePromise : Promise < void > | undefined ;
10851114
10861115 constructor (
10871116 snapshotId : number ,
@@ -1117,10 +1146,14 @@ export class Program implements FormatDiagnosticsHost {
11171146 }
11181147
11191148 [ globalThis . Symbol . dispose ] ( ) : void {
1120- this . dispose ( ) ;
1149+ void this . dispose ( ) ;
1150+ }
1151+
1152+ dispose ( ) : Promise < void > {
1153+ return this . disposePromise ??= this . disposeWorker ( ) ;
11211154 }
11221155
1123- async dispose ( ) : Promise < void > {
1156+ private async disposeWorker ( ) : Promise < void > {
11241157 const snapshot = this . ownedSnapshot ;
11251158 this . ownedSnapshot = undefined ;
11261159 if ( snapshot ) await snapshot . dispose ( ) ;
0 commit comments