Skip to content

Commit cc9a366

Browse files
committed
feat: better rust api to convertedExpr
1 parent 5af1690 commit cc9a366

File tree

5 files changed

+398
-273
lines changed

5 files changed

+398
-273
lines changed

glass-easel-miniprogram-typescript/src/cli.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { parseArgs } from 'util'
22
import * as ts from 'typescript'
3+
import { TmplGroup } from 'glass-easel-template-compiler'
34
import { type Diagnostic, DiagnosticLevel, Server } from './server'
45

56
const { values } = parseArgs({
@@ -60,32 +61,36 @@ const logDiagnostic = (diag: Diagnostic) => {
6061
}
6162
}
6263

64+
let success = true
65+
6366
const server = new Server({
6467
typescriptNodeModule: ts,
68+
tmplGroup: new TmplGroup(),
6569
projectPath: values.path,
6670
verboseMessages: values.verbose,
6771
strictMode: values.strict,
6872
onFirstScanDone() {
69-
let success = true
7073
this.getConfigErrors().forEach((diag) => {
7174
success = false
7275
logDiagnostic(diag)
7376
})
74-
if (success) {
75-
this.listTrackingComponents().forEach((compPath) => {
76-
const diags = this.analyzeWxmlFile(`${compPath}.wxml`)
77-
if (diags.length) success = false
78-
diags.forEach(logDiagnostic)
79-
})
80-
}
81-
if (!values.watch) {
82-
server.end()
83-
if (!success) process.exit(1)
84-
}
77+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
78+
;(async () => {
79+
await this.waitPendingAsyncTasks()
80+
if (!values.watch) {
81+
server.end()
82+
if (!success) process.exit(1)
83+
}
84+
return undefined
85+
})()
8586
},
8687
onDiagnosticsNeedUpdate(fullPath: string) {
87-
if (!values.watch) return
88-
const diags = this.analyzeWxmlFile(fullPath)
89-
diags.forEach(logDiagnostic)
88+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
89+
;(async () => {
90+
const diags = await this.analyzeWxmlFile(fullPath)
91+
if (diags.length) success = false
92+
diags.forEach(logDiagnostic)
93+
return undefined
94+
})()
9095
},
9196
})

glass-easel-miniprogram-typescript/src/project.ts

Lines changed: 126 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
11
import path from 'node:path'
22
import 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'
44
import { 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+
638
export type Position = {
739
line: number
840
character: number
@@ -102,16 +134,25 @@ type ConvertedExprCache = {
102134
export 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

Comments
 (0)