Skip to content

Commit 9d3d511

Browse files
committed
feat: add global template backend config support
1 parent 59e74b5 commit 9d3d511

File tree

4 files changed

+92
-9
lines changed

4 files changed

+92
-9
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ComponentProperties as _GlobalComponentProperties_ } from '../glass-easel-miniprogram-typescript/sample-template-backend-config.d.ts'
2+
import type component from './index'
3+
import type _component_view from './../../components/view/view'
4+
import type _component_image from './../../components/image/image'
5+
6+
type _Component_<P, W, M> = { propertyValues: P, dataWithProperties: W, methods: M }
7+
type _ComponentFieldTypes_<T> = (T & Record<string, never>)['_$fieldTypes']
8+
interface UnknownElement { _$fieldTypes: { propertyValues: Record<string, never>, dataWithProperties: Record<string, never>, methods: Record<string, never> } }
9+
10+
type _Properties_<T> = _ComponentFieldTypes_<T> extends _Component_<infer P, any, any>
11+
? P
12+
: { [k: string]: any }
13+
14+
declare const data: _ComponentFieldTypes_<typeof component> extends _Component_<any, infer W, any>
15+
? W
16+
: { [k: string]: any }
17+
18+
declare const methods: _ComponentFieldTypes_<typeof component> extends _Component_<any, any, infer M>
19+
? M
20+
: { [k: string]: any }
21+
22+
declare const tags: _GlobalComponentProperties_ & {
23+
'view': _Properties_<typeof _component_view>;
24+
'image': _Properties_<typeof _component_image>;
25+
[other: string]: unknown }
26+
export default {}
27+
28+
type _ForIndex_<T> = T extends any[] ? number : T extends { [key: string | symbol]: any } ? string | symbol : number;
29+
type _ForItem_<T> = T extends (infer T)[] ? T : T extends { [key: string | symbol]: infer V } ? V : any;
30+
type _ForKey_<T, N extends string> = N extends "*this" ? _ForItem_<T> : _ForItem_<T> extends { [k: string]: any } ? _ForItem_<T>[N] : unknown;
31+
{const _tag_=tags['view'];{const _tag_=tags['image'];var _string_or_number_:string|number=_tag_.src;var _class_:string="logo";}}{const _tag_=tags['view'];var _class_:string="hello";var _event_:Function=methods.helloTap;}{const _tag_=tags['view'];_tag_.hidden=!data.showAgain;var _class_:string="hello";}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export type GlassEaselTemplateBackendConfig = {
2+
name: 'sample'
3+
description: 'An example of template backend configuration.'
4+
majorVersion: 1
5+
minorVersion: 0
6+
}
7+
8+
export type ComponentProperties = {
9+
div: {
10+
hidden: boolean
11+
}
12+
img: {
13+
src: string
14+
}
15+
}

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { parseArgs } from 'util'
2+
import fs from 'node:fs'
23
import * as ts from 'typescript'
34
import { TmplGroup } from 'glass-easel-template-compiler'
45
import { type Diagnostic, DiagnosticLevel, Server } from './server'
@@ -26,6 +27,10 @@ const { values } = parseArgs({
2627
type: 'boolean',
2728
short: 'w',
2829
},
30+
templateBackendConfig: {
31+
type: 'string',
32+
short: 'c',
33+
},
2934
},
3035
strict: true,
3136
})
@@ -35,11 +40,12 @@ if (values.help) {
3540
console.log(`Usage: miniprogram-typescript-check [options]
3641
3742
Options:
38-
-h, --help Show this help message and exit
39-
-p, --path <path> The path of the mini-program project (default: .)
40-
-w, --watch Watch the project and analyze on change
41-
-s, --strict Enable strict mode (avoid using \`any\` types when possible)
42-
-v, --verbose Print verbose messages
43+
-h, --help Show this help message and exit
44+
-p, --path <path> The path of the mini-program project (default: .)
45+
-c, --templateBackendConfig <path> The path of the template backend config (including the extension)
46+
-w, --watch Watch the project and analyze on change
47+
-s, --strict Enable strict mode (avoid using \`any\` types when possible)
48+
-v, --verbose Print verbose messages
4349
`)
4450
process.exit(0)
4551
}
@@ -67,6 +73,7 @@ const server = new Server({
6773
typescriptNodeModule: ts,
6874
tmplGroup: new TmplGroup(),
6975
projectPath: values.path,
76+
templateBackendConfigPath: values.templateBackendConfig,
7077
verboseMessages: values.verbose,
7178
strictMode: values.strict,
7279
onFirstScanDone() {

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export type ServerOptions = {
8282
projectPath: string
8383
/** the working directory */
8484
workingDirectory?: string
85+
/** the path to the template backend configuration */
86+
templateBackendConfigPath?: string
87+
/** the content of the template backend configuration */
88+
templateBackendConfig?: string
8589
/** whether to print verbose messages */
8690
verboseMessages?: boolean
8791
/** Whether to enable strict checks (avoid `any` type fallbacks) */
@@ -103,6 +107,8 @@ export class Server {
103107
private rootFilePaths: Cache<string[]>
104108
private rootTsFileCount: number
105109
private rootFilePathsVersion: number
110+
private templateBackendConfigPath: string
111+
private templateBackendConfig: ts.IScriptSnapshot | null
106112
private configErrors: ts.Diagnostic[]
107113
private pendingAsyncTasksListeners: (() => void)[] = []
108114

@@ -111,9 +117,15 @@ export class Server {
111117
const tsc = this.tsc
112118
this.tmplGroup = options.tmplGroup
113119
const tmplGroup = this.tmplGroup
114-
this.options = options
115120
this.workingDirectory = options.workingDirectory ?? process.cwd()
116121
this.projectPath = path.resolve(this.workingDirectory, options.projectPath)
122+
this.templateBackendConfig = options.templateBackendConfig
123+
? tsc.ScriptSnapshot.fromString(options.templateBackendConfig)
124+
: null
125+
this.templateBackendConfigPath = options.templateBackendConfigPath
126+
? path.resolve(this.workingDirectory, options.templateBackendConfigPath)
127+
: ''
128+
this.options = options
117129

118130
// initialize virtual file system
119131
this.projectDirManager = new ProjectDirManager(tsc, tmplGroup, this.projectPath, () => {
@@ -217,9 +229,19 @@ export class Server {
217229
return this.rootFilePaths.get()
218230
},
219231
getScriptVersion: (fullPath) => {
232+
if (fullPath === this.options.templateBackendConfigPath) {
233+
if (this.templateBackendConfig !== null) {
234+
return '1'
235+
}
236+
}
220237
return this.projectDirManager.getFileVersion(fullPath)?.toString() ?? ''
221238
},
222239
getScriptSnapshot: (fullPath) => {
240+
if (fullPath === this.options.templateBackendConfigPath) {
241+
if (this.templateBackendConfig !== null) {
242+
return this.templateBackendConfig
243+
}
244+
}
223245
const content = this.projectDirManager.getFileTsContent(fullPath)
224246
if (content === null) return undefined
225247
return tsc.ScriptSnapshot.fromString(content)
@@ -302,6 +324,13 @@ export class Server {
302324
// escape helper
303325
const escapeJsString = (str: string) => str.replace(/['\\]/g, (a) => `\\${a}`)
304326

327+
// get global backend configuraion fields
328+
const globalBackendConfigLine = this.templateBackendConfigPath
329+
? `import type { ComponentProperties as _GlobalComponentProperties_ } from '${escapeJsString(
330+
this.templateBackendConfigPath,
331+
)}'`
332+
: 'type _GlobalComponentProperties_ = Record<string, never>'
333+
305334
// get exports from corresponding ts file
306335
const defaultExport = getDefaultExportOfSourceFile(tc, source)
307336
const relPath = path.relative(compDir, tsFullPath.slice(0, -3))
@@ -322,7 +351,7 @@ declare const methods: _ComponentFieldTypes_<typeof component> extends _Componen
322351

323352
// get exports from using components
324353
const propertiesHelperLine = `
325-
type Properties<T> = _ComponentFieldTypes_<T> extends _Component_<infer P, any, any>
354+
type _Properties_<T> = _ComponentFieldTypes_<T> extends _Component_<infer P, any, any>
326355
? P
327356
: { [k: string]: any }`
328357
let usingComponentsImports = ''
@@ -336,7 +365,7 @@ type Properties<T> = _ComponentFieldTypes_<T> extends _Component_<infer P, any,
336365
const relPath = path.relative(compDir, compPath)
337366
const entryName = `_component_${tagName.replace(/-/g, '_')}`
338367
usingComponentsImports += `import type ${entryName} from './${escapeJsString(relPath)}'\n`
339-
usingComponentsItems.push(`'${tagName}': Properties<typeof ${entryName}>;\n`)
368+
usingComponentsItems.push(`'${tagName}': _Properties_<typeof ${entryName}>;\n`)
340369
})
341370

342371
// treat generics as any type tags
@@ -355,13 +384,14 @@ type Properties<T> = _ComponentFieldTypes_<T> extends _Component_<infer P, any,
355384
? '[other: string]: unknown'
356385
: '[other: string]: any'
357386
const tagsLine = `
358-
declare const tags: {
387+
declare const tags: _GlobalComponentProperties_ & {
359388
${usingComponentsItems.join('')}${otherComponents} }`
360389

361390
// add an empty export to avoid some tsc behavior
362391
const exportLine = 'export default {}'
363392

364393
return [
394+
globalBackendConfigLine,
365395
tsImportLine,
366396
usingComponentsImports,
367397
adapterTypesLine,

0 commit comments

Comments
 (0)