11import path from 'node:path'
22import type * as ts from 'typescript'
3- import { type TmplConvertedExpr , TmplGroup } from 'glass-easel-template-compiler'
3+ import type { TmplGroup as CompilerTmplGroup } from 'glass-easel-template-compiler'
44import { VirtualFileSystem } from './virtual_fs'
55
6+ export interface TmplGroup {
7+ free : CompilerTmplGroup [ 'free' ]
8+ addTmpl : CompilerTmplGroup [ 'addTmpl' ]
9+ getTmplConvertedExpr : (
10+ path : string ,
11+ tsEnv : string ,
12+ ) => Promise < TmplConvertedExpr > | TmplConvertedExpr
13+ }
14+
15+ export interface TmplConvertedExpr {
16+ free ( ) : void
17+ code ( ) : string | undefined
18+ getSourceLocation : (
19+ startLine : number ,
20+ startCol : number ,
21+ endLine : number ,
22+ endCol : number ,
23+ ) =>
24+ | Promise < [ number , number , number , number ] | Uint32Array | undefined >
25+ | [ number , number , number , number ]
26+ | Uint32Array
27+ | undefined
28+ getTokenAtSourcePosition : (
29+ line : number ,
30+ col : number ,
31+ ) =>
32+ | Promise < [ number , number , number , number , number , number ] | Uint32Array | undefined >
33+ | [ number , number , number , number , number , number ]
34+ | Uint32Array
35+ | undefined
36+ }
37+
638export type Position = {
739 line : number
840 character : number
@@ -102,16 +134,25 @@ type ConvertedExprCache = {
102134export class ProjectDirManager {
103135 readonly vfs : VirtualFileSystem
104136 private tsc : typeof ts
137+ private tmplGroup : TmplGroup
105138 private readonly rootPath : string
106- private tmplGroup = new TmplGroup ( )
107139 private trackingComponents = Object . create ( null ) as Record < string , ComponentJsonData >
108140 private convertedExpr = Object . create ( null ) as Record < string , ConvertedExprCache >
141+ private pendingAsyncTasks = 0
142+ wxmlEnvGetter : ( tsFullPath : string , wxmlFullPath : string ) => string | null = ( ) => null
109143 onEntranceFileAdded : ( fullPath : string ) => void = ( ) => { }
110144 onEntranceFileRemoved : ( fullPath : string ) => void = ( ) => { }
111- onConvertedExprCacheInvalidated : ( wxmlFullPath : string ) => void = ( ) => { }
112-
113- constructor ( tsc : typeof ts , rootPath : string , firstScanCallback : ( ) => void ) {
145+ onConvertedExprCacheUpdated : ( wxmlFullPath : string ) => void = ( ) => { }
146+ onPendingAsyncTasksEmpty : ( ) => void = ( ) => { }
147+
148+ constructor (
149+ tsc : typeof ts ,
150+ tmplGroup : TmplGroup ,
151+ rootPath : string ,
152+ firstScanCallback : ( ) => void ,
153+ ) {
114154 this . tsc = tsc
155+ this . tmplGroup = tmplGroup
115156 this . rootPath = rootPath
116157 this . vfs = new VirtualFileSystem ( rootPath , firstScanCallback )
117158 this . vfs . onFileFound = ( fullPath ) => {
@@ -131,13 +172,18 @@ export class ProjectDirManager {
131172 return this . vfs . stop ( )
132173 }
133174
175+ pendingAsyncTasksCount ( ) {
176+ return this . pendingAsyncTasks
177+ }
178+
134179 private handleFileOpened ( fullPath : string ) {
135180 const compPath = possibleComponentPath ( fullPath )
136181 if ( compPath !== null ) {
137182 if ( isJsonFile ( fullPath ) ) {
138183 const isComp = ! ! this . trackingComponents [ compPath ]
139184 const newIsComp = this . updateComponentJsonData ( fullPath ) !== null
140185 if ( ! isComp && newIsComp ) {
186+ this . asyncWxmlConvertedExprUpdate ( `${ compPath } .wxml` )
141187 this . onEntranceFileAdded ( `${ compPath } .wxml` )
142188 }
143189 }
@@ -148,21 +194,24 @@ export class ProjectDirManager {
148194 const compPath = possibleComponentPath ( fullPath )
149195 if ( compPath !== null ) {
150196 if ( isJsonFile ( fullPath ) ) {
151- this . removeConvertedExprCache ( `${ compPath } .wxml` )
152197 const isComp = ! ! this . trackingComponents [ compPath ]
153198 const newIsComp = this . updateComponentJsonData ( fullPath ) !== null
154- if ( ! isComp && newIsComp ) {
155- this . onEntranceFileAdded ( `${ compPath } .wxml` )
156- } else if ( isComp && ! newIsComp ) {
199+ if ( newIsComp ) {
200+ this . asyncWxmlConvertedExprUpdate ( `${ compPath } .wxml` )
201+ if ( ! isComp ) {
202+ this . onEntranceFileAdded ( `${ compPath } .wxml` )
203+ }
204+ } else if ( isComp ) {
205+ this . deleteConvertedExprCache ( `${ compPath } .wxml` )
157206 this . onEntranceFileRemoved ( `${ compPath } .wxml` )
158207 }
159208 } else if ( isTsFile ( fullPath ) ) {
160- this . removeConvertedExprCache ( `${ compPath } .wxml` )
209+ this . checkConvertedExprCache ( `${ compPath } .wxml` )
161210 this . forEachDirectDependantComponents ( compPath , ( compPath ) => {
162- this . removeConvertedExprCache ( `${ compPath } .wxml` )
211+ this . checkConvertedExprCache ( `${ compPath } .wxml` )
163212 } )
164213 } else {
165- this . removeConvertedExprCache ( fullPath )
214+ this . checkConvertedExprCache ( fullPath )
166215 }
167216 }
168217 }
@@ -171,18 +220,18 @@ export class ProjectDirManager {
171220 const compPath = possibleComponentPath ( fullPath )
172221 if ( compPath !== null ) {
173222 if ( isJsonFile ( fullPath ) ) {
174- this . removeConvertedExprCache ( `${ compPath } .wxml` )
175223 const isComp = ! ! this . trackingComponents [ compPath ]
176224 if ( isComp ) {
225+ this . deleteConvertedExprCache ( `${ compPath } .wxml` )
177226 this . onEntranceFileRemoved ( `${ compPath } .wxml` )
178227 }
179228 } else if ( isTsFile ( fullPath ) ) {
180- this . removeConvertedExprCache ( `${ compPath } .wxml` )
229+ this . checkConvertedExprCache ( `${ compPath } .wxml` )
181230 this . forEachDirectDependantComponents ( compPath , ( compPath ) => {
182- this . removeConvertedExprCache ( `${ compPath } .wxml` )
231+ this . checkConvertedExprCache ( `${ compPath } .wxml` )
183232 } )
184233 } else {
185- this . removeConvertedExprCache ( fullPath )
234+ this . deleteConvertedExprCache ( fullPath )
186235 }
187236 }
188237 }
@@ -191,28 +240,31 @@ export class ProjectDirManager {
191240 return this . vfs . trackFile ( fullPath )
192241 }
193242
194- removeConvertedExprCache ( fullPath : string ) {
195- if ( isWxmlFile ( fullPath ) ) {
196- const ce = this . convertedExpr [ fullPath ]
197- if ( ce ) {
198- ce . expr ?. free ( )
199- ce . expr = undefined
200- ce . source = undefined
201- ce . version += 1
202- this . onConvertedExprCacheInvalidated ( fullPath )
203- }
243+ private checkConvertedExprCache ( fullPath : string ) {
244+ if ( ! isWxmlFile ( fullPath ) ) return
245+ if ( this . trackingComponents [ fullPath . slice ( 0 , - 5 ) ] ) {
246+ this . asyncWxmlConvertedExprUpdate ( fullPath )
247+ } else {
248+ this . deleteConvertedExprCache ( fullPath )
204249 }
205250 }
206251
207- getWxmlConvertedExpr (
208- wxmlFullPath : string ,
209- wxmlEnvGetter : ( fullPath : string , importTargetFullPath : string ) => string | null ,
210- ) : string | null {
211- const tsFullPath = ` ${ wxmlFullPath . slice ( 0 , - 5 ) } .ts`
212- let cache = this . convertedExpr [ wxmlFullPath ]
213- if ( cache ?. expr ) {
214- return cache . expr . code ( ) ?? ''
252+ private deleteConvertedExprCache ( fullPath : string ) {
253+ const ce = this . convertedExpr [ fullPath ]
254+ if ( ce && ce . expr ) {
255+ ce . expr ?. free ( )
256+ ce . expr = undefined
257+ ce . source = undefined
258+ ce . version += 1
259+ this . onConvertedExprCacheUpdated ( fullPath )
215260 }
261+ }
262+
263+ private asyncWxmlConvertedExprUpdate ( wxmlFullPath : string ) {
264+ const compPath = wxmlFullPath . slice ( 0 , - 5 )
265+ const tsFullPath = `${ compPath } .ts`
266+ let cache = this . convertedExpr [ wxmlFullPath ]
267+ if ( cache ?. expr ) return
216268 if ( ! cache ) {
217269 cache = {
218270 expr : undefined ,
@@ -222,49 +274,64 @@ export class ProjectDirManager {
222274 this . convertedExpr [ wxmlFullPath ] = cache
223275 }
224276 const content = this . getFileContent ( wxmlFullPath )
225- if ( ! content ) return null
277+ if ( ! content ) return
226278 const relPath = path . relative ( this . vfs . rootPath , wxmlFullPath ) . split ( path . sep ) . join ( '/' )
227- if ( relPath . startsWith ( '../' ) ) return null
279+ if ( relPath . startsWith ( '../' ) ) return
228280 this . tmplGroup . addTmpl ( relPath , content )
229- const env = wxmlEnvGetter ( tsFullPath , wxmlFullPath )
230- if ( ! env ) return null
231- const expr = this . tmplGroup . getTmplConvertedExpr ( relPath , env )
232- cache . expr = expr
233- cache . source = this . tsc . createSourceFile ( wxmlFullPath , content , this . tsc . ScriptTarget . Latest )
234- cache . version += 1
235- return expr . code ( ) ?? ''
281+ const env = this . wxmlEnvGetter ( tsFullPath , wxmlFullPath )
282+ if ( ! env ) return
283+ this . pendingAsyncTasks += 1
284+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
285+ ; ( async ( ) => {
286+ try {
287+ const expr = await this . tmplGroup . getTmplConvertedExpr ( relPath , env )
288+ cache . expr = expr
289+ cache . source = this . tsc . createSourceFile (
290+ wxmlFullPath ,
291+ content ,
292+ this . tsc . ScriptTarget . Latest ,
293+ )
294+ cache . version += 1
295+ } finally {
296+ this . pendingAsyncTasks -= 1
297+ if ( this . pendingAsyncTasks === 0 ) {
298+ this . onPendingAsyncTasksEmpty ( )
299+ }
300+ }
301+ this . onConvertedExprCacheUpdated ( wxmlFullPath )
302+ } ) ( )
236303 }
237304
238- getTokenInfoAtPosition (
305+ async getTokenInfoAtPosition (
239306 wxmlFullPath : string ,
240307 pos : Position ,
241- ) : { sourceStart : Position ; sourceEnd : Position ; dest : Position } | null {
242- const arr = this . convertedExpr [ wxmlFullPath ] ?. expr ?. getTokenAtSourcePosition (
308+ ) : Promise < { sourceStart : Position ; sourceEnd : Position ; dest : Position } | null > {
309+ const arr = await this . convertedExpr [ wxmlFullPath ] ?. expr ?. getTokenAtSourcePosition (
243310 pos . line ,
244311 pos . character ,
245312 )
246313 if ( ! arr ) return null
247314 return {
248- sourceStart : { line : arr [ 0 ] ! , character : arr [ 1 ] ! } ,
249- sourceEnd : { line : arr [ 2 ] ! , character : arr [ 3 ] ! } ,
250- dest : { line : arr [ 4 ] ! , character : arr [ 5 ] ! } ,
315+ sourceStart : { line : arr [ 0 ] , character : arr [ 1 ] } ,
316+ sourceEnd : { line : arr [ 2 ] , character : arr [ 3 ] } ,
317+ dest : { line : arr [ 4 ] , character : arr [ 5 ] } ,
251318 }
252319 }
253320
254- getWxmlSource (
321+ async getWxmlSource (
255322 wxmlFullPath : string ,
256323 startLine : number ,
257324 startCol : number ,
258325 endLine : number ,
259326 endCol : number ,
260- ) : {
327+ ) : Promise < {
261328 source : ts . SourceFile
262329 startLine : number
263330 startCol : number
264331 endLine : number
265332 endCol : number
266- } | null {
267- const arr = this . convertedExpr [ wxmlFullPath ] ?. expr ?. getSourceLocation (
333+ } | null > {
334+ const arr = await this . convertedExpr [ wxmlFullPath ] ?. expr ?. getSourceLocation (
268335 startLine ,
269336 startCol ,
270337 endLine ,
@@ -273,20 +340,17 @@ export class ProjectDirManager {
273340 if ( ! arr ) return null
274341 return {
275342 source : this . convertedExpr [ wxmlFullPath ] ! . source ! ,
276- startLine : arr [ 0 ] ! ,
277- startCol : arr [ 1 ] ! ,
278- endLine : arr [ 2 ] ! ,
279- endCol : arr [ 3 ] ! ,
343+ startLine : arr [ 0 ] ,
344+ startCol : arr [ 1 ] ,
345+ endLine : arr [ 2 ] ,
346+ endCol : arr [ 3 ] ,
280347 }
281348 }
282349
283- getFileTsContent (
284- fullPath : string ,
285- wxmlEnvGetter : ( tsFullPath : string , wxmlFullPath : string ) => string | null ,
286- ) : string | null {
350+ getFileTsContent ( fullPath : string ) : string | null {
287351 const p = getWxmlTsPathReverted ( fullPath )
288352 if ( p !== null ) {
289- return this . getWxmlConvertedExpr ( p , wxmlEnvGetter ) ?? ''
353+ return this . convertedExpr [ p ] ?. expr ?. code ( ) ?? ''
290354 }
291355 return this . getFileContent ( fullPath )
292356 }
0 commit comments