idlize
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
16import * as ts from 'ohos-typescript'17import * as path from 'path'18import { getDeclarationsByNode, getDecorator, hasDecorator, id, isCallDecorator, isDecorator, prependComment, Void } from './ApiUtils'19
20export const ComponentDecorator = "Component"21export const EntryDecorator = "Entry"22export const LocalStoragePropertyName = "_entry_local_storage_"23
24export const StateDecorator = "State"25export const PropDecorator = "Prop"26export const LinkDecorator = "Link"27export const ObjectLinkDecorator = "ObjectLink"28export const ProvideDecorator = "Provide"29export const ConsumeDecorator = "Consume"30export const StorageLinkDecorator = "StorageLink"31export const StoragePropDecorator = "StorageProp"32export const LocalStorageLinkDecorator = "LocalStorageLink"33export const LocalStoragePropDecorator = "LocalStorageProp"34export const BuilderParamDecorator = "BuilderParam"35export const BuilderDecorator = "Builder"36export const BuilderLambdaDecorator = "BuilderLambda"37export const WatchDecorator = "Watch"38export const CustomDialogDecorator = "CustomDialog"39
40export const StylesDecorator = "Styles"41export const ExtendDecorator = "Extend"42export const AnimatableExtendDecorator = "AnimatableExtend"43export const ArkCommonMethod = "ArkCommonMethod"44export const T_TypeParameter = "T"45export const CommonInstance = "CommonInstance"46export const Instance = "Instance"47
48export enum SyncedPropertyConstructor {49propState = "propState",50objectLink = "objectLinkState"51}
52export type SyncedPropertyImplemented = typeof PropDecorator | typeof ObjectLinkDecorator | typeof StoragePropDecorator | typeof LocalStoragePropDecorator53
54export enum RewriteNames {55Initializers = "initializers",56InitializeStruct = "__initializeStruct",57UpdateStruct = "__updateStruct",58ApplyStyle = "__applyStyle",59ApplyAnimatableExtend = "__applyAnimatableExtend"60}
61
62export class NameTable {63structs: string[] = []64dollars: string[] = []65}
66
67export class IssueTable {68newEtsWithProperty: number[] = []69newEts: number[] = []70builderLambda: number[] = []71}
72
73export class CallTable {74builderLambdas = new Map<ts.CallExpression, string>()75globalBuilderCalls = new Set<ts.CallExpression>()76legacyCalls = new Set<ts.CallExpression>()77structCalls = new Set<ts.CallExpression>()78lazyCalls = new Set<ts.Identifier>()79}
80
81export function prependMemoComment<T extends ts.Node>(node: T): T {82return prependComment(node, "* @memo ")83}
84
85export function prependDoubleLineMemoComment<T extends ts.Node>(node: T): T {86return prependComment(prependComment(node, ""), "* @memo ")87}
88
89export function relativeToSource(sourcePath: string, sourceDir: string): string {90return path.relative(sourceDir, sourcePath)91}
92
93export function emitStartupFile(entryFile: string, sourceDir: string, destinationDir: string) {94const absoluteSourceDir = path.resolve(sourceDir)95const absoluteEntryFile = path.resolve(entryFile)96const relativeEntryFile = path.relative(absoluteSourceDir, absoluteEntryFile)97if (!relativeEntryFile.endsWith(".ets")) return98
99const importPath = relativeEntryFile.substring(0, relativeEntryFile.length - 4)100console.log("IMPORT FROM: " + importPath)101
102const mainFile = path.join(destinationDir, "main.ts")103console.log("STARTUP: " + mainFile)104
105}
106
107export function typeParameterExtendsType(name: string, superName: string, superArguments: string[]) {108return ts.factory.createTypeParameterDeclaration(109id(name),110ts.factory.createTypeReferenceNode(111id(superName),112superArguments.map(113it => ts.factory.createTypeReferenceNode(114id(it)115)116)117),118undefined119)120}
121
122export function mangleIfBuild(name: ts.PropertyName): ts.PropertyName {123if (!ts.isIdentifier(name)) return name124const stringName = ts.idText(name)125if (stringName === "build") {126return id("_build")127} else {128return name129}130}
131
132export function isNamedDeclaration(node: ts.Node): node is ts.NamedDeclaration {133return ("name" in node)134}
135
136/** @returns true if the given node represents an identifier */
137export function isTextIdentifier(node: ts.Node): node is ts.Identifier | ts.PrivateIdentifier {138return ts.isIdentifier(node) || ts.isPrivateIdentifier(node)139}
140
141/** @returns text identifier from the specified node */
142export function idTextOrError(node: ts.Node): string {143if (isTextIdentifier(node)) return ts.idText(node)144throw new Error("Expected an identifier, got: " + ts.SyntaxKind[node.kind])145}
146
147export function asString(node: ts.Node | undefined): string {148if (node === undefined) return "undefined node"149if (isTextIdentifier(node)) return ts.idText(node)150if (ts.isEtsComponentExpression(node)) return `EtsComponentExpression(${ts.idText(node.expression as ts.Identifier)}`151if (isNamedDeclaration(node)) {152if (node.name === undefined) {153return `${ts.SyntaxKind[node.kind]}(undefined name)`154} else {155return `${ts.SyntaxKind[node.kind]}(${asString(node.name)})`156}157} else {158return `${ts.SyntaxKind[node.kind]}`159}160}
161
162export function adaptorName(original: string): string {163return `Ark${original}`164}
165
166export function adaptorComponentName(original: string): string {167return `Ark${original}Component`168}
169
170export function etsAttributeName(original: string): string {171const attribute = `${original}Attribute`172return attribute.startsWith("Lazy") ? attribute.substring(4) : attribute173}
174
175export function parameterName(original: string): string {176return "instance"177}
178
179export function backingField(originalName: string): string {180return `_${originalName}`181}
182
183export function backingFieldName(originalName: ts.Identifier | ts.PrivateIdentifier): ts.Identifier {184return id(backingField(ts.idText(originalName)))185}
186
187export function adaptorEtsName(name: ts.Identifier): ts.Identifier {188return id(adaptorName(ts.idText(name)))189}
190
191export function adaptorEtsParameterName(name: ts.Identifier): ts.Identifier {192return id(parameterName(ts.idText(name)))193}
194
195export function adaptorEtsAttributeName(name: ts.Identifier): ts.Identifier {196return id(etsAttributeName(ts.idText(name)))197}
198
199export function adaptorClassName(name: ts.Identifier | undefined): ts.Identifier | undefined {200if (!name) return undefined201return id(adaptorComponentName(ts.idText(name)))202}
203
204export function customDialogImplName(name: ts.Identifier | undefined): ts.Identifier | undefined {205if (!name) return undefined206return id(ts.idText(name) + "Impl")207}
208
209export function deduceBuilderLambdaName(functionDeclaration: ts.FunctionDeclaration): string {210const decorator = getDecorator(functionDeclaration, BuilderLambdaDecorator)211if (!decorator) throw new Error("Expected a decorator")212
213if (ts.isCallExpression(decorator.expression)) {214const name = decorator.expression.arguments[0]!215if (ts.isIdentifier(name)) {216return ts.idText(name)217}218if (ts.isStringLiteral(name)) {219return name.text220}221}222throw new Error("Unexpected decorator kind: " + asString(decorator.expression))223}
224
225export function findBuilderLambdaRedirect(typechecker: ts.TypeChecker, node: ts.Node): string|undefined {226if (!ts.isCallExpression(node)) return undefined227const func = node.expression228if (!ts.isIdentifier(func)) return undefined229
230const declarations = getDeclarationsByNode(typechecker, func)231
232if (declarations.length == 0) return undefined233const firstDeclaration = declarations[0]234if (!firstDeclaration) return undefined235if (!isBuilderLambda(firstDeclaration)) return undefined236
237return deduceBuilderLambdaName(firstDeclaration as ts.FunctionDeclaration)238}
239
240export function isBuilderLambdaCall(callTable: CallTable, node: ts.CallExpression): boolean {241const originalNode = ts.getOriginalNode(node) as ts.CallExpression242return callTable.builderLambdas.has(originalNode)243}
244
245export function isStructCall(callTable: CallTable, node: ts.CallExpression): boolean {246const originalNode = ts.getOriginalNode(node) as ts.CallExpression247return callTable.structCalls.has(originalNode)248}
249
250export function deduceProvideConsumeName(property: ts.PropertyDeclaration, name: string): string {251const decorator = getDecorator(property, name)252if (!decorator) throw new Error("Expected a decorator")253
254// @Provide foo255if (ts.isIdentifier(decorator.expression)) {256return idTextOrError(property.name)257}258// @Provide("bar") foo259if (ts.isCallExpression(decorator.expression)) {260const arg = decorator.expression.arguments[0]!261if (ts.isIdentifier(arg)) {262return ts.idText(arg)263}264if (ts.isStringLiteral(arg)) {265return arg.text266}267}268
269throw new Error("Unexpected decorator kind: " + asString(decorator.expression))270}
271
272export function deduceProvideName(property: ts.PropertyDeclaration) {273return deduceProvideConsumeName(property, ProvideDecorator)274}
275
276export function deduceConsumeName(property: ts.PropertyDeclaration) {277return deduceProvideConsumeName(property, ConsumeDecorator)278}
279
280export function getAnnotationArgument(decorator: ts.Decorator): ts.Identifier | undefined {281if (!ts.isCallExpression(decorator.expression)) return undefined282const args = decorator.expression.arguments283if (args.length > 1) return undefined284const argument = args[0]285if (!ts.isIdentifier(argument)) return undefined286return argument287}
288
289export function isExtend(decorator: ts.Decorator): boolean {290return isCallDecorator(decorator, ExtendDecorator)291}
292
293export function isState(property: ts.PropertyDeclaration): boolean {294return hasDecorator(property, StateDecorator)295}
296
297export function isLink(property: ts.PropertyDeclaration): boolean {298return hasDecorator(property, LinkDecorator)299}
300
301export function isProp(property: ts.PropertyDeclaration): boolean {302return hasDecorator(property, PropDecorator)303}
304
305export function isObjectLink(property: ts.PropertyDeclaration): boolean {306return hasDecorator(property, ObjectLinkDecorator)307}
308
309export function isConsume(property: ts.PropertyDeclaration): boolean {310return hasDecorator(property, ConsumeDecorator)311}
312
313export function isStorageProp(property: ts.PropertyDeclaration): boolean {314return hasDecorator(property, StoragePropDecorator)315}
316
317export function isStorageLink(property: ts.PropertyDeclaration): boolean {318return hasDecorator(property, StorageLinkDecorator)319}
320
321export function isLocalStorageProp(property: ts.PropertyDeclaration): boolean {322return hasDecorator(property, LocalStoragePropDecorator)323}
324
325export function isLocalStorageLink(property: ts.PropertyDeclaration): boolean {326return hasDecorator(property, LocalStorageLinkDecorator)327}
328
329export function isBuilder(property: ts.MethodDeclaration): boolean {330return hasDecorator(property, BuilderDecorator)331}
332
333export function isBuilderParam(property: ts.PropertyDeclaration): boolean {334return hasDecorator(property, BuilderParamDecorator)335}
336
337export function isProvide(property: ts.PropertyDeclaration): boolean {338return hasDecorator(property, ProvideDecorator)339}
340
341export function dropLink(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {342return dropDecorators(modifierLikes, LinkDecorator)343}
344
345export function dropProp(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {346return dropDecorators(modifierLikes, PropDecorator)347}
348
349export function dropObjectLink(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {350return dropDecorators(modifierLikes, ObjectLinkDecorator)351}
352
353export function dropWatch(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {354return dropDecorators(modifierLikes, WatchDecorator)355}
356
357export function dropConsume(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {358return dropDecorators(modifierLikes, ConsumeDecorator)359}
360
361export function dropProvide(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {362return dropDecorators(modifierLikes, ProvideDecorator)363}
364
365export function dropBuilder(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {366return dropDecorators(modifierLikes, BuilderDecorator)367}
368
369export function dropStylesLike(modifierLikes: readonly ts.ModifierLike[] | undefined): ts.ModifierLike[] | undefined {370return dropDecorators(modifierLikes, StylesDecorator, ExtendDecorator, AnimatableExtendDecorator)371}
372
373export function dropDecorators(modifierLikes: readonly ts.ModifierLike[] | undefined, ...decorators: (string | undefined)[]): ts.ModifierLike[] | undefined {374const defined = decorators.filter((it): it is string => it !== undefined)375return modifierLikes?.filter(it => defined.every(decorator => !isDecorator(it, decorator)))376}
377
378export function filterDecorators(node: ts.Node): readonly ts.Decorator[] | undefined {379return ts.getAllDecorators(node)380}
381
382export function filterModifiers(node: ts.Node): readonly ts.Modifier[] | undefined {383if (!ts.canHaveModifiers(node)) {384return undefined385}386return ts.getModifiers(node)387}
388
389export function contextLocalStateOf(name: string, initializer: ts.Expression, type?: ts.TypeNode): ts.Expression {390return ts.factory.createCallExpression(391id("contextLocalStateOf"),392type ? [type] : undefined,393[394ts.factory.createStringLiteral(name),395ts.factory.createArrowFunction(396undefined,397undefined,398[],399undefined,400ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),401initializer
402)403]404)405}
406
407export function isGlobalBuilder(node: ts.Node): boolean {408return ts.isFunctionDeclaration(node) && hasDecorator(node, BuilderDecorator)409}
410
411export function isBuilderLambda(node: ts.Node): boolean {412return ts.isFunctionDeclaration(node) && hasDecorator(node, BuilderLambdaDecorator)413}
414
415export function provideConsumeVariableName(decorator: string, prefix: string, property: ts.PropertyDeclaration): ts.Identifier {416if (!ts.isIdentifier(property.name) && !ts.isPrivateIdentifier(property.name)) {417throw new Error("Expected an identifier")418}419
420return id(prefix + deduceProvideConsumeName(property, decorator))421}
422
423export function consumeVariableName(consume: ts.PropertyDeclaration): ts.Identifier {424return provideConsumeVariableName(ConsumeDecorator, "__consume_", consume)425}
426
427export function provideVariableName(provide: ts.PropertyDeclaration): ts.Identifier {428return provideConsumeVariableName(ProvideDecorator, "__provide_", provide)429}
430
431export function createProvideInitializerField(provide: ts.PropertyDeclaration): ts.PropertyAssignment {432return ts.factory.createPropertyAssignment(433backingFieldName(provide.name as ts.Identifier),434provideVariableName(provide)435)436}
437
438export function createConsumeInitializerField(consume: ts.PropertyDeclaration): ts.PropertyAssignment {439return ts.factory.createPropertyAssignment(440backingFieldName(consume.name as ts.Identifier),441consumeVariableName(consume)442)443}
444
445export function filterDecoratedProperties(members: ts.NodeArray<ts.ClassElement>, decoratorName: string): ts.PropertyDeclaration[] {446return members.filter(property => ts.isPropertyDeclaration(property) && hasDecorator(property, decoratorName)) as ts.PropertyDeclaration[]447}
448
449export function filterLinks(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {450return members.filter(it =>451ts.isPropertyDeclaration(it) && isLink(it)452) as ts.PropertyDeclaration[]453}
454
455export function filterProps(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {456return members.filter(it =>457ts.isPropertyDeclaration(it) && isProp(it)458) as ts.PropertyDeclaration[]459}
460
461export function filterObjectLinks(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {462return members.filter(it =>463ts.isPropertyDeclaration(it) && isObjectLink(it)464) as ts.PropertyDeclaration[]465}
466
467export function filterConsumes(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {468return members.filter(it =>469ts.isPropertyDeclaration(it) && isConsume(it)470) as ts.PropertyDeclaration[]471}
472
473export function filterProvides(members: ts.NodeArray<ts.ClassElement>): ts.PropertyDeclaration[] {474return members.filter(it =>475ts.isPropertyDeclaration(it) && isProvide(it)476) as ts.PropertyDeclaration[]477}
478
479/** @returns a nullable accessor: `expression?.name` */
480export function createNullableAccessor(expression: ts.Expression, name: string): ts.Expression {481return ts.factory.createPropertyAccessChain(expression, ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), name)482}
483
484/** @returns a not-null accessor: `expression!.name!` */
485export function createNotNullAccessor(expression: ts.Expression, name: string): ts.Expression {486return ts.factory.createNonNullExpression(ts.factory.createPropertyAccessExpression(ts.factory.createNonNullExpression(expression), name))487}
488
489/** @returns a multiline block with the given statements */
490export function createBlock(...statements: ts.Statement[]): ts.Block {491return ts.factory.createBlock(statements, true)492}
493
494/** @returns a nullish coalescing expression with safe right part: `left ?? (right)` */
495export function createNullishCoalescing(left: ts.Expression, right: ts.Expression): ts.Expression {496return ts.factory.createBinaryExpression(left, ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createParenthesizedExpression(right))497}
498
499export function createValueAccessor(expression: ts.Expression): ts.Expression {500return ts.factory.createPropertyAccessExpression(expression, "value")501}
502
503export function filterDefined<T>(value: (T | undefined)[]): T[] {504return value.filter((it: T | undefined): it is T => it != undefined)505}
506
507export function collect<T>(...value: (ReadonlyArray<T> | T | undefined)[]): T[] {508const empty: (T | undefined)[] = []509return filterDefined(empty.concat(...value))510}
511
512export function initializers() {513return id(RewriteNames.Initializers)514}
515
516export function isDefined<T>(value: T | undefined | null): value is T {517return value !== undefined && value !== null518}
519
520export function extendsLikeFunctionName(propertyName: string, componentName: string): string {521return propertyName + "__" + componentName522}
523
524export function hasLocalDeclaration(typechecker: ts.TypeChecker, node: ts.Identifier): boolean {525const declarations = getDeclarationsByNode(typechecker, node)526return (declarations.length == 1 && declarations[0].getSourceFile() == node.getSourceFile())527}
528
529export function isBuiltinComponentName(ctx: ts.TransformationContext, name: string) {530return ctx.getCompilerOptions().ets?.components.includes(name)531}
532
533export function voidLambdaType() {534return ts.factory.createFunctionTypeNode(535undefined,536[],537Void()538)539}