idlize

Форк
0
539 строк · 19.5 Кб
1
/*
2
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15

16
import * as ts from 'ohos-typescript'
17
import * as path from 'path'
18
import { getDeclarationsByNode, getDecorator, hasDecorator, id, isCallDecorator, isDecorator, prependComment, Void } from './ApiUtils'
19

20
export const ComponentDecorator = "Component"
21
export const EntryDecorator = "Entry"
22
export const LocalStoragePropertyName = "_entry_local_storage_"
23

24
export const StateDecorator = "State"
25
export const PropDecorator = "Prop"
26
export const LinkDecorator = "Link"
27
export const ObjectLinkDecorator = "ObjectLink"
28
export const ProvideDecorator = "Provide"
29
export const ConsumeDecorator = "Consume"
30
export const StorageLinkDecorator = "StorageLink"
31
export const StoragePropDecorator = "StorageProp"
32
export const LocalStorageLinkDecorator = "LocalStorageLink"
33
export const LocalStoragePropDecorator = "LocalStorageProp"
34
export const BuilderParamDecorator = "BuilderParam"
35
export const BuilderDecorator = "Builder"
36
export const BuilderLambdaDecorator = "BuilderLambda"
37
export const WatchDecorator = "Watch"
38
export const CustomDialogDecorator = "CustomDialog"
39

40
export const StylesDecorator = "Styles"
41
export const ExtendDecorator = "Extend"
42
export const AnimatableExtendDecorator = "AnimatableExtend"
43
export const ArkCommonMethod = "ArkCommonMethod"
44
export const T_TypeParameter = "T"
45
export const CommonInstance = "CommonInstance"
46
export const Instance = "Instance"
47

48
export enum SyncedPropertyConstructor {
49
    propState = "propState",
50
    objectLink = "objectLinkState"
51
}
52
export type SyncedPropertyImplemented = typeof PropDecorator | typeof ObjectLinkDecorator | typeof StoragePropDecorator | typeof LocalStoragePropDecorator
53

54
export enum RewriteNames {
55
    Initializers = "initializers",
56
    InitializeStruct = "__initializeStruct",
57
    UpdateStruct = "__updateStruct",
58
    ApplyStyle = "__applyStyle",
59
    ApplyAnimatableExtend = "__applyAnimatableExtend"
60
}
61

62
export class NameTable {
63
    structs: string[] = []
64
    dollars: string[] = []
65
}
66

67
export class IssueTable {
68
    newEtsWithProperty: number[] = []
69
    newEts: number[] = []
70
    builderLambda: number[] = []
71
}
72

73
export class CallTable {
74
    builderLambdas = new Map<ts.CallExpression, string>()
75
    globalBuilderCalls = new Set<ts.CallExpression>()
76
    legacyCalls = new Set<ts.CallExpression>()
77
    structCalls = new Set<ts.CallExpression>()
78
    lazyCalls = new Set<ts.Identifier>()
79
}
80

81
export function prependMemoComment<T extends ts.Node>(node: T): T {
82
    return prependComment(node, "* @memo ")
83
}
84

85
export function prependDoubleLineMemoComment<T extends ts.Node>(node: T): T {
86
    return prependComment(prependComment(node, ""), "* @memo ")
87
}
88

89
export function relativeToSource(sourcePath: string, sourceDir: string): string {
90
    return path.relative(sourceDir, sourcePath)
91
}
92

93
export function emitStartupFile(entryFile: string, sourceDir: string, destinationDir: string) {
94
    const absoluteSourceDir = path.resolve(sourceDir)
95
    const absoluteEntryFile = path.resolve(entryFile)
96
    const relativeEntryFile = path.relative(absoluteSourceDir, absoluteEntryFile)
97
    if (!relativeEntryFile.endsWith(".ets")) return
98

99
    const importPath = relativeEntryFile.substring(0, relativeEntryFile.length - 4)
100
    console.log("IMPORT FROM: " + importPath)
101

102
    const mainFile = path.join(destinationDir, "main.ts")
103
    console.log("STARTUP: " + mainFile)
104

105
}
106

107
export function typeParameterExtendsType(name: string, superName: string, superArguments: string[]) {
108
    return ts.factory.createTypeParameterDeclaration(
109
        id(name),
110
        ts.factory.createTypeReferenceNode(
111
            id(superName),
112
            superArguments.map(
113
                it => ts.factory.createTypeReferenceNode(
114
                    id(it)
115
                )
116
            )
117
        ),
118
        undefined
119
    )
120
}
121

122
export function mangleIfBuild(name: ts.PropertyName): ts.PropertyName {
123
    if (!ts.isIdentifier(name)) return name
124
    const stringName = ts.idText(name)
125
    if (stringName === "build") {
126
        return id("_build")
127
    } else {
128
        return name
129
    }
130
}
131

132
export function isNamedDeclaration(node: ts.Node): node is ts.NamedDeclaration {
133
    return ("name" in node)
134
}
135

136
/** @returns true if the given node represents an identifier */
137
export function isTextIdentifier(node: ts.Node): node is ts.Identifier | ts.PrivateIdentifier {
138
    return ts.isIdentifier(node) || ts.isPrivateIdentifier(node)
139
}
140

141
/** @returns text identifier from the specified node */
142
export function idTextOrError(node: ts.Node): string {
143
    if (isTextIdentifier(node)) return ts.idText(node)
144
    throw new Error("Expected an identifier, got: " + ts.SyntaxKind[node.kind])
145
}
146

147
export function asString(node: ts.Node | undefined): string {
148
    if (node === undefined) return "undefined node"
149
    if (isTextIdentifier(node)) return ts.idText(node)
150
    if (ts.isEtsComponentExpression(node)) return `EtsComponentExpression(${ts.idText(node.expression as ts.Identifier)}`
151
    if (isNamedDeclaration(node)) {
152
        if (node.name === undefined) {
153
            return `${ts.SyntaxKind[node.kind]}(undefined name)`
154
        } else {
155
            return `${ts.SyntaxKind[node.kind]}(${asString(node.name)})`
156
        }
157
    } else {
158
        return `${ts.SyntaxKind[node.kind]}`
159
    }
160
}
161

162
export function adaptorName(original: string): string {
163
    return `Ark${original}`
164
}
165

166
export function adaptorComponentName(original: string): string {
167
    return `Ark${original}Component`
168
}
169

170
export function etsAttributeName(original: string): string {
171
    const attribute = `${original}Attribute`
172
    return attribute.startsWith("Lazy") ? attribute.substring(4) : attribute
173
}
174

175
export function parameterName(original: string): string {
176
    return "instance"
177
}
178

179
export function backingField(originalName: string): string {
180
    return `_${originalName}`
181
}
182

183
export function backingFieldName(originalName: ts.Identifier | ts.PrivateIdentifier): ts.Identifier {
184
    return id(backingField(ts.idText(originalName)))
185
}
186

187
export function adaptorEtsName(name: ts.Identifier): ts.Identifier {
188
    return id(adaptorName(ts.idText(name)))
189
}
190

191
export function adaptorEtsParameterName(name: ts.Identifier): ts.Identifier {
192
    return id(parameterName(ts.idText(name)))
193
}
194

195
export function adaptorEtsAttributeName(name: ts.Identifier): ts.Identifier {
196
    return id(etsAttributeName(ts.idText(name)))
197
}
198

199
export function adaptorClassName(name: ts.Identifier | undefined): ts.Identifier | undefined {
200
    if (!name) return undefined
201
    return id(adaptorComponentName(ts.idText(name)))
202
}
203

204
export function customDialogImplName(name: ts.Identifier | undefined): ts.Identifier | undefined {
205
    if (!name) return undefined
206
    return id(ts.idText(name) + "Impl")
207
}
208

209
export function deduceBuilderLambdaName(functionDeclaration: ts.FunctionDeclaration): string {
210
    const decorator = getDecorator(functionDeclaration, BuilderLambdaDecorator)
211
    if (!decorator) throw new Error("Expected a decorator")
212

213
    if (ts.isCallExpression(decorator.expression)) {
214
        const name = decorator.expression.arguments[0]!
215
        if (ts.isIdentifier(name)) {
216
            return ts.idText(name)
217
        }
218
        if (ts.isStringLiteral(name)) {
219
            return name.text
220
        }
221
    }
222
    throw new Error("Unexpected decorator kind: " + asString(decorator.expression))
223
}
224

225
export function findBuilderLambdaRedirect(typechecker: ts.TypeChecker, node: ts.Node): string|undefined {
226
    if (!ts.isCallExpression(node)) return undefined
227
    const func = node.expression
228
    if (!ts.isIdentifier(func)) return undefined
229

230
    const declarations = getDeclarationsByNode(typechecker, func)
231

232
    if (declarations.length == 0) return undefined
233
    const firstDeclaration = declarations[0]
234
    if (!firstDeclaration) return undefined
235
    if (!isBuilderLambda(firstDeclaration)) return undefined
236

237
    return deduceBuilderLambdaName(firstDeclaration as ts.FunctionDeclaration)
238
}
239

240
export function isBuilderLambdaCall(callTable: CallTable, node: ts.CallExpression): boolean {
241
    const originalNode = ts.getOriginalNode(node) as ts.CallExpression
242
    return callTable.builderLambdas.has(originalNode)
243
}
244

245
export function isStructCall(callTable: CallTable, node: ts.CallExpression): boolean {
246
    const originalNode = ts.getOriginalNode(node) as ts.CallExpression
247
    return callTable.structCalls.has(originalNode)
248
}
249

250
export function deduceProvideConsumeName(property: ts.PropertyDeclaration, name: string): string {
251
    const decorator = getDecorator(property, name)
252
    if (!decorator) throw new Error("Expected a decorator")
253

254
    // @Provide foo
255
    if (ts.isIdentifier(decorator.expression)) {
256
        return idTextOrError(property.name)
257
    }
258
    // @Provide("bar") foo
259
    if (ts.isCallExpression(decorator.expression)) {
260
        const arg = decorator.expression.arguments[0]!
261
        if (ts.isIdentifier(arg)) {
262
            return ts.idText(arg)
263
        }
264
        if (ts.isStringLiteral(arg)) {
265
            return arg.text
266
        }
267
    }
268

269
    throw new Error("Unexpected decorator kind: " + asString(decorator.expression))
270
}
271

272
export function deduceProvideName(property: ts.PropertyDeclaration) {
273
    return deduceProvideConsumeName(property, ProvideDecorator)
274
}
275

276
export function deduceConsumeName(property: ts.PropertyDeclaration) {
277
    return deduceProvideConsumeName(property, ConsumeDecorator)
278
}
279

280
export function getAnnotationArgument(decorator: ts.Decorator): ts.Identifier | undefined {
281
    if (!ts.isCallExpression(decorator.expression)) return undefined
282
    const args = decorator.expression.arguments
283
    if (args.length > 1) return undefined
284
    const argument = args[0]
285
    if (!ts.isIdentifier(argument)) return undefined
286
    return argument
287
}
288

289
export function isExtend(decorator: ts.Decorator): boolean {
290
    return isCallDecorator(decorator, ExtendDecorator)
291
}
292

293
export function isState(property: ts.PropertyDeclaration): boolean {
294
    return hasDecorator(property, StateDecorator)
295
}
296

297
export function isLink(property: ts.PropertyDeclaration): boolean {
298
    return hasDecorator(property, LinkDecorator)
299
}
300

301
export function isProp(property: ts.PropertyDeclaration): boolean {
302
    return hasDecorator(property, PropDecorator)
303
}
304

305
export function isObjectLink(property: ts.PropertyDeclaration): boolean {
306
    return hasDecorator(property, ObjectLinkDecorator)
307
}
308

309
export function isConsume(property: ts.PropertyDeclaration): boolean {
310
    return hasDecorator(property, ConsumeDecorator)
311
}
312

313
export function isStorageProp(property: ts.PropertyDeclaration): boolean {
314
    return hasDecorator(property, StoragePropDecorator)
315
}
316

317
export function isStorageLink(property: ts.PropertyDeclaration): boolean {
318
    return hasDecorator(property, StorageLinkDecorator)
319
}
320

321
export function isLocalStorageProp(property: ts.PropertyDeclaration): boolean {
322
    return hasDecorator(property, LocalStoragePropDecorator)
323
}
324

325
export function isLocalStorageLink(property: ts.PropertyDeclaration): boolean {
326
    return hasDecorator(property, LocalStorageLinkDecorator)
327
}
328

329
export function isBuilder(property: ts.MethodDeclaration): boolean {
330
    return hasDecorator(property, BuilderDecorator)
331
}
332

333
export function isBuilderParam(property: ts.PropertyDeclaration): boolean {
334
    return hasDecorator(property, BuilderParamDecorator)
335
}
336

337
export function isProvide(property: ts.PropertyDeclaration): boolean {
338
    return hasDecorator(property, ProvideDecorator)
339
}
340

341
export function dropLink(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
342
    return dropDecorators(modifierLikes, LinkDecorator)
343
}
344

345
export function dropProp(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
346
    return dropDecorators(modifierLikes, PropDecorator)
347
}
348

349
export function dropObjectLink(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
350
    return dropDecorators(modifierLikes, ObjectLinkDecorator)
351
}
352

353
export function dropWatch(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
354
    return dropDecorators(modifierLikes, WatchDecorator)
355
}
356

357
export function dropConsume(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
358
    return dropDecorators(modifierLikes, ConsumeDecorator)
359
}
360

361
export function dropProvide(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
362
    return dropDecorators(modifierLikes, ProvideDecorator)
363
}
364

365
export function dropBuilder(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
366
    return dropDecorators(modifierLikes, BuilderDecorator)
367
}
368

369
export function dropStylesLike(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {
370
    return dropDecorators(modifierLikes, StylesDecorator, ExtendDecorator, AnimatableExtendDecorator)
371
}
372

373
export function dropDecorators(modifierLikes: readonly ts.ModifierLike[] | undefined, ...decorators: (string | undefined)[]): ts.ModifierLike[] | undefined {
374
    const defined = decorators.filter((it): it is string => it !== undefined)
375
    return modifierLikes?.filter(it => defined.every(decorator => !isDecorator(it, decorator)))
376
}
377

378
export function filterDecorators(node: ts.Node): readonly ts.Decorator[] | undefined {
379
    return ts.getAllDecorators(node)
380
}
381

382
export function filterModifiers(node: ts.Node): readonly ts.Modifier[] | undefined {
383
    if (!ts.canHaveModifiers(node)) {
384
        return undefined
385
    }
386
    return ts.getModifiers(node)
387
}
388

389
export function contextLocalStateOf(name: string, initializer: ts.Expression, type?: ts.TypeNode): ts.Expression {
390
    return ts.factory.createCallExpression(
391
        id("contextLocalStateOf"),
392
        type ? [type] : undefined,
393
        [
394
            ts.factory.createStringLiteral(name),
395
            ts.factory.createArrowFunction(
396
                undefined,
397
                undefined,
398
                [],
399
                undefined,
400
                ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
401
                initializer
402
            )
403
        ]
404
    )
405
}
406

407
export function isGlobalBuilder(node: ts.Node): boolean {
408
    return ts.isFunctionDeclaration(node) && hasDecorator(node, BuilderDecorator)
409
}
410

411
export function isBuilderLambda(node: ts.Node): boolean {
412
    return ts.isFunctionDeclaration(node) && hasDecorator(node, BuilderLambdaDecorator)
413
}
414

415
export function provideConsumeVariableName(decorator: string, prefix: string, property: ts.PropertyDeclaration): ts.Identifier {
416
    if (!ts.isIdentifier(property.name) && !ts.isPrivateIdentifier(property.name)) {
417
        throw new Error("Expected an identifier")
418
    }
419

420
    return id(prefix + deduceProvideConsumeName(property, decorator))
421
}
422

423
export function consumeVariableName(consume: ts.PropertyDeclaration): ts.Identifier {
424
    return provideConsumeVariableName(ConsumeDecorator, "__consume_", consume)
425
}
426

427
export function provideVariableName(provide: ts.PropertyDeclaration): ts.Identifier {
428
    return provideConsumeVariableName(ProvideDecorator, "__provide_", provide)
429
}
430

431
export function createProvideInitializerField(provide: ts.PropertyDeclaration): ts.PropertyAssignment {
432
    return ts.factory.createPropertyAssignment(
433
        backingFieldName(provide.name as ts.Identifier),
434
        provideVariableName(provide)
435
    )
436
}
437

438
export function createConsumeInitializerField(consume: ts.PropertyDeclaration): ts.PropertyAssignment {
439
    return ts.factory.createPropertyAssignment(
440
        backingFieldName(consume.name as ts.Identifier),
441
        consumeVariableName(consume)
442
    )
443
}
444

445
export function filterDecoratedProperties(members: ts.NodeArray<ts.ClassElement>, decoratorName: string): ts.PropertyDeclaration[] {
446
    return members.filter(property => ts.isPropertyDeclaration(property) && hasDecorator(property, decoratorName)) as ts.PropertyDeclaration[]
447
}
448

449
export function filterLinks(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {
450
    return members.filter(it =>
451
        ts.isPropertyDeclaration(it) && isLink(it)
452
    ) as ts.PropertyDeclaration[]
453
}
454

455
export function filterProps(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {
456
    return members.filter(it =>
457
        ts.isPropertyDeclaration(it) && isProp(it)
458
    ) as ts.PropertyDeclaration[]
459
}
460

461
export function filterObjectLinks(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {
462
    return members.filter(it =>
463
        ts.isPropertyDeclaration(it) && isObjectLink(it)
464
    ) as ts.PropertyDeclaration[]
465
}
466

467
export function filterConsumes(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {
468
    return members.filter(it =>
469
        ts.isPropertyDeclaration(it) && isConsume(it)
470
    ) as ts.PropertyDeclaration[]
471
}
472

473
export function filterProvides(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {
474
    return members.filter(it =>
475
        ts.isPropertyDeclaration(it) && isProvide(it)
476
    ) as ts.PropertyDeclaration[]
477
}
478

479
/** @returns a nullable accessor: `expression?.name` */
480
export function createNullableAccessor(expression: ts.Expression, name: string): ts.Expression {
481
    return ts.factory.createPropertyAccessChain(expression, ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), name)
482
}
483

484
/** @returns a not-null accessor: `expression!.name!` */
485
export function createNotNullAccessor(expression: ts.Expression, name: string): ts.Expression {
486
    return ts.factory.createNonNullExpression(ts.factory.createPropertyAccessExpression(ts.factory.createNonNullExpression(expression), name))
487
}
488

489
/** @returns a multiline block with the given statements */
490
export function createBlock(...statements: ts.Statement[]): ts.Block {
491
    return ts.factory.createBlock(statements, true)
492
}
493

494
/** @returns a nullish coalescing expression with safe right part: `left ?? (right)` */
495
export function createNullishCoalescing(left: ts.Expression, right: ts.Expression): ts.Expression {
496
    return ts.factory.createBinaryExpression(left, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createParenthesizedExpression(right))
497
}
498

499
export function createValueAccessor(expression: ts.Expression): ts.Expression {
500
    return ts.factory.createPropertyAccessExpression(expression, "value")
501
}
502

503
export function filterDefined<T>(value: (T | undefined)[]): T[] {
504
    return value.filter((it: T | undefined): it is T => it != undefined)
505
}
506

507
export function collect<T>(...value: (ReadonlyArray<T> | T | undefined)[]): T[] {
508
    const empty: (T | undefined)[] = []
509
    return filterDefined(empty.concat(...value))
510
}
511

512
export function initializers() {
513
    return id(RewriteNames.Initializers)
514
}
515

516
export function isDefined<T>(value: T | undefined | null): value is T {
517
    return value !== undefined && value !== null
518
}
519

520
export function extendsLikeFunctionName(propertyName: string, componentName: string): string {
521
    return propertyName + "__" + componentName
522
}
523

524
export function hasLocalDeclaration(typechecker: ts.TypeChecker, node: ts.Identifier): boolean {
525
    const declarations = getDeclarationsByNode(typechecker, node)
526
    return (declarations.length == 1 && declarations[0].getSourceFile() == node.getSourceFile())
527
}
528

529
export function isBuiltinComponentName(ctx: ts.TransformationContext, name: string) {
530
    return ctx.getCompilerOptions().ets?.components.includes(name)
531
}
532

533
export function voidLambdaType() {
534
    return ts.factory.createFunctionTypeNode(
535
        undefined,
536
        [],
537
        Void()
538
    )
539
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.