From 28682760fff729b5959aaeefed8202aa5b3fc048 Mon Sep 17 00:00:00 2001 From: Cuecuexiaoyu Date: Fri, 13 Jun 2025 09:46:18 +0800 Subject: [PATCH] develop component and componentV2 Signed-off-by: Cuecuexiaoyu Change-Id: I795c62cc2653a7b249fc8c0cc5c7e61b6f69a937 --- arkui-plugins/common/predefines.ts | 3 + .../builder-lambda-translators/factory.ts | 42 +++---- .../ui-plugins/component-transformer.ts | 76 +++++++++---- .../ui-plugins/property-translators/utils.ts | 9 +- .../ui-plugins/struct-translators/factory.ts | 104 ++++++------------ .../ui-plugins/struct-translators/utils.ts | 16 --- arkui-plugins/ui-plugins/ui-factory.ts | 44 ++++++-- arkui-plugins/ui-plugins/utils.ts | 65 +++++++++-- .../src/arkts-api/factory/nodeFactory.ts | 8 ++ .../node-utilities/TSClassImplements.ts | 31 ++++++ koala-wrapper/src/generated/index.ts | 1 + 11 files changed, 245 insertions(+), 154 deletions(-) create mode 100644 koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 7aa7f645d..1dcb07f26 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -50,7 +50,10 @@ export enum BindableDecl { export enum StructDecoratorNames { ENTRY = 'Entry', COMPONENT = 'Component', + COMPONENT_V2 = 'ComponentV2', RESUABLE = 'Reusable', + RESUABLE_V2 = 'ReusableV2', + CUSTOM_LAYOUT = 'CustomLayout', } export enum DecoratorNames { diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index a9177f4ef..8d3d688f1 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -20,8 +20,9 @@ import { CustomComponentNames, findCanAddMemoFromParamExpression, isCustomComponentAnnotation, + hasMemoAnnotation, } from '../utils'; -import { backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; +import { annotation, backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; import { BuilderLambdaDeclInfo, builderLambdaFunctionName, @@ -42,29 +43,13 @@ import { isStyleWithReceiverCall, builderLambdaType, } from './utils'; -import { isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; +import { hasDecorator, isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; import { factory as PropertyFactory } from '../property-translators/factory'; import { ProjectConfig } from '../../common/plugin-context'; -import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames } from '../../common/predefines'; +import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames, DecoratorNames } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; export class factory { - /** - * generate `reuseKey?: string` in `@ComponentBuilder` $_instantiate - */ - static createReusableKeyArgForCustomComponent(): arkts.ETSParameterExpression { - return arkts.factory - .createParameterDeclaration( - arkts.factory.createIdentifier( - 'reuseKey', - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('string')) - ) - ), - undefined - ) - .setOptional(true); - } /** * generate `packageInfo: string` in `@ComponentBuilder` XComponent @@ -94,10 +79,7 @@ export class factory { const func: arkts.ScriptFunction = node.scriptFunction; let newParams: arkts.Expression[] = [styleArg]; if (func.params.length > 0) { - newParams.push(...func.params.slice(0, func.params.length - 1)); - if (node.name.name === BuilderLambdaNames.ORIGIN_METHOD_NAME) { - newParams.push(this.createReusableKeyArgForCustomComponent()); - } + newParams.push(...this.updateBuilderParameters(func.params).slice(0, func.params.length - 1)); if (externalSourceName === 'arkui.component.xcomponent' && node.name.name === 'XComponent') { newParams.push(this.createPackageInfoArgForXComponent()); } @@ -122,12 +104,24 @@ export class factory { node, node.kind, arkts.factory.updateIdentifier(node.name, newName ?? node.name.name), - updateFunc, + node.name.name === BuilderLambdaNames.ORIGIN_METHOD_NAME ? addMemoAnnotation(updateFunc) : updateFunc, node.modifiers, false ); } + /** + * update `@Builder` decorated parameter expression. + */ + static updateBuilderParameters(params: readonly arkts.Expression[]): arkts.Expression[] { + return params.map((item: arkts.Expression) => { + if (arkts.isEtsParameterExpression(item) && hasDecorator(item, DecoratorNames.BUILDER)) { + return addMemoAnnotation(item); + } + return item; + }); + } + /* * transform arguments in style node. */ diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index 1bde82161..ac7ea9cf6 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -69,7 +69,10 @@ export class ComponentTransformer extends AbstractVisitor { private entryNames: string[] = []; private structMembersMap: Map = new Map(); private isCustomComponentImported: boolean = false; + private isCustomComponentV2Imported: boolean = false; private isEntryPointImported: boolean = false; + private isPageLifeCycleImported: boolean = false; + private isLayoutCallbackImported: boolean = false; private shouldAddLinkIntrinsic: boolean = false; private hasLegacy: boolean = false; private legacyStructMap: Map = new Map(); @@ -87,7 +90,10 @@ export class ComponentTransformer extends AbstractVisitor { this.entryNames = []; this.structMembersMap = new Map(); this.isCustomComponentImported = false; + this.isCustomComponentV2Imported = false; this.isEntryPointImported = false; + this.isPageLifeCycleImported = false; + this.isLayoutCallbackImported = false; this.shouldAddLinkIntrinsic = false; this.hasLegacy = false; this.legacyStructMap = new Map(); @@ -108,6 +114,13 @@ export class ComponentTransformer extends AbstractVisitor { CustomComponentNames.COMPONENT_CLASS_NAME ); } + if (arkts.isETSImportDeclaration(node) && !this.isCustomComponentV2Imported) { + this.isCustomComponentV2Imported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + } if (arkts.isETSImportDeclaration(node) && !this.isEntryPointImported) { this.isEntryPointImported = !!findLocalImport( node, @@ -115,6 +128,20 @@ export class ComponentTransformer extends AbstractVisitor { EntryWrapperNames.ENTRY_POINT_CLASS_NAME ); } + if (arkts.isETSImportDeclaration(node) && !this.isPageLifeCycleImported) { + this.isPageLifeCycleImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.PAGE_LIFE_CYCLE + ); + } + if (arkts.isETSImportDeclaration(node) && !this.isLayoutCallbackImported) { + this.isLayoutCallbackImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.LAYOUT_CALLBACK + ); + } } exit(node: arkts.AstNode) { @@ -126,9 +153,9 @@ export class ComponentTransformer extends AbstractVisitor { } } - createImportDeclaration(): void { - const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME); - const imported: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CLASS_NAME); + createImportDeclaration(sourceName: string, importedName: string): void { + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(sourceName); + const imported: arkts.Identifier = arkts.factory.createIdentifier(importedName); // Insert this import at the top of the script's statements. if (!this.program) { throw Error('Failed to insert import: Transformer has no program'); @@ -152,7 +179,18 @@ export class ComponentTransformer extends AbstractVisitor { updateStatements.push(factory.createIntrinsicAnnotationDeclaration({ expr })); } if (this.componentInterfaceCollection.length > 0) { - if (!this.isCustomComponentImported) this.createImportDeclaration(); + if (!this.isCustomComponentImported) + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_CLASS_NAME + ); + if (!this.isCustomComponentV2Imported) + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + if (!this.isLayoutCallbackImported) + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.LAYOUT_CALLBACK); updateStatements.push(...this.componentInterfaceCollection); } @@ -160,6 +198,8 @@ export class ComponentTransformer extends AbstractVisitor { if (!this.isEntryPointImported) entryFactory.createAndInsertEntryPointImport(this.program); // normally, we should only have at most one @Entry component in a single file. // probably need to handle error message here. + if (!this.isPageLifeCycleImported) + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.PAGE_LIFE_CYCLE); updateStatements.push(...this.entryNames.map(entryFactory.generateEntryWrapper)); } if (updateStatements.length > 0) { @@ -210,20 +250,16 @@ export class ComponentTransformer extends AbstractVisitor { if (!className || scopeInfo?.name !== className) { return node; } - arkts.insertGlobalStructInfo(className); - if (arkts.isStructDeclaration(node)) { this.collectComponentMembers(node, className); } - const customComponentInterface = this.generateComponentInterface( className, node.modifiers | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT, Object.values(scopeInfo.annotations ?? {}).map((anno) => anno.clone()) ); this.componentInterfaceCollection.push(customComponentInterface); - const definition: arkts.ClassDefinition = node.definition!; const newDefinitionBody: arkts.AstNode[] = []; if (!!scopeInfo.annotations?.entry) { @@ -269,26 +305,24 @@ export class ComponentTransformer extends AbstractVisitor { staticMethodBody.push(buildCompatibleNode); } } + const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; + const extendsName: string = scopeInfo.annotations.component + ? CustomComponentNames.COMPONENT_CLASS_NAME + : CustomComponentNames.COMPONENT_V2_CLASS_NAME; return arkts.factory .createClassDefinition( definition.ident, undefined, undefined, // superTypeParams doen't work - definition.implements, + [...definition.implements, ...factory.generateImplementsForStruct(scopeInfo.annotations)], undefined, arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CLASS_NAME), + arkts.factory.createIdentifier(extendsName), arkts.factory.createTSTypeParameterInstantiation([ - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(className)) - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier( - `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}` - ) - ) + factory.createTypeReferenceFromString(className), + factory.createTypeReferenceFromString( + `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}` ), ]) ) @@ -407,9 +441,7 @@ export class ComponentTransformer extends AbstractVisitor { const context: InteropContext = { className: className, path: path, - arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression - ? args[0] - : undefined + arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression ? args[0] : undefined, }; return generateInstantiateInterop(context); } diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index 2a750395a..08c0fb11f 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -23,7 +23,7 @@ import { DecoratorNames, DECORATOR_TYPE_MAP, StateManagementTypes, - GetSetTypes + GetSetTypes, } from '../../common/predefines'; import { addMemoAnnotation, findCanAddMemoFromParamExpression, findCanAddMemoFromTypeAnnotation } from '../utils'; @@ -94,7 +94,12 @@ export function hasDecoratorName( } export function hasDecorator( - property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + property: + | arkts.ClassProperty + | arkts.ClassDefinition + | arkts.MethodDefinition + | arkts.ETSParameterExpression + | arkts.ETSFunctionType, decoratorName: DecoratorNames ): boolean { if (arkts.isMethodDefinition(property)) { diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 3eed150d6..6e5ddff03 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -23,6 +23,7 @@ import { getTypeParamsFromClassDecl, isCustomComponentInterface, MemoNames, + isKnownMethodDefinition, } from '../utils'; import { factory as uiFactory } from '../ui-factory'; import { factory as propertyFactory } from '../property-translators/factory'; @@ -35,7 +36,7 @@ import { InterfacePropertyTranslator, PropertyTranslator, } from '../property-translators'; -import { CustomComponentScopeInfo, isEtsGlobalClass, isKnownMethodDefinition } from './utils'; +import { CustomComponentScopeInfo, isEtsGlobalClass } from './utils'; import { collectStateManagementTypeImport, hasDecorator, PropertyCache } from '../property-translators/utils'; import { ProjectConfig } from '../../common/plugin-context'; import { DeclarationCollector } from '../../common/declaration-collector'; @@ -60,47 +61,6 @@ export class factory { return member; } - /* - * create _build method. - */ - static transformBuildMethodWithOriginBuild( - method: arkts.MethodDefinition, - typeName: string, - optionsName: string, - isDecl?: boolean - ): arkts.MethodDefinition { - const updateKey: arkts.Identifier = arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_BUILD); - - const scriptFunction: arkts.ScriptFunction = method.scriptFunction; - const updateScriptFunction = arkts.factory.createScriptFunction( - scriptFunction.body, - arkts.FunctionSignature.createFunctionSignature( - scriptFunction.typeParams, - [ - uiFactory.createStyleParameter(typeName), - uiFactory.createContentParameter(), - uiFactory.createInitializersOptionsParameter(optionsName), - ], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), - false - ), - scriptFunction.flags, - scriptFunction.modifiers - ); - addMemoAnnotation(updateScriptFunction); - - const modifiers: arkts.Es2pandaModifierFlags = isDecl - ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT - : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - updateKey, - updateScriptFunction, - modifiers, - false - ); - } - /* * generate _r() or _rawfile(). */ @@ -441,24 +401,14 @@ export class factory { /** * transform non-property members in custom-component class. */ - static transformNonPropertyMembersInClass( - member: arkts.AstNode, - classTypeName: string | undefined, - classOptionsName: string | undefined, - className: string, - isDecl?: boolean - ): arkts.AstNode { + static transformNonPropertyMembersInClass(member: arkts.AstNode, isDecl?: boolean): arkts.AstNode { if (arkts.isMethodDefinition(member)) { propertyFactory.addMemoToBuilderClassMethod(member); if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && !isDecl) { return this.setStructConstructorToPrivate(member); - } else if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { - return this.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); + } + if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { + addMemoAnnotation(member.scriptFunction); } return member; } @@ -472,11 +422,9 @@ export class factory { if (!node.definition) { return node; } - let classTypeName: string | undefined; let classOptionsName: string | undefined; if (scope.isDecl) { - const [classType, classOptions] = getTypeParamsFromClassDecl(node); - classTypeName = getTypeNameFromTypeParameter(classType); + const [_, classOptions] = getTypeParamsFromClassDecl(node); classOptionsName = getTypeNameFromTypeParameter(classOptions); } const definition: arkts.ClassDefinition = node.definition; @@ -495,15 +443,7 @@ export class factory { ); const updateMembers: arkts.AstNode[] = definition.body .filter((member) => !arkts.isClassProperty(member)) - .map((member: arkts.AstNode) => - factory.transformNonPropertyMembersInClass( - member, - classTypeName, - classOptionsName, - className, - scope.isDecl - ) - ); + .map((member: arkts.AstNode) => factory.transformNonPropertyMembersInClass(member, scope.isDecl)); const updateClassDef: arkts.ClassDefinition = this.updateCustomComponentClass(definition, [ ...translatedMembers, @@ -546,7 +486,25 @@ export class factory { if (isCustomComponentInterface(node)) { return factory.tranformCustomComponentInterfaceMembers(node); } - return node; + return factory.tranformInterfaceBuildMember(node); + } + + static tranformInterfaceBuildMember(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + const newBody: arkts.AstNode[] = node.body!.body.map((it) => { + if (arkts.isMethodDefinition(it)) { + propertyFactory.addMemoToBuilderClassMethod(it); + } + return it; + }); + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + arkts.factory.updateInterfaceBody(node.body!, newBody), + node.isStatic, + node.isFromExternal + ); } /** @@ -619,9 +577,13 @@ export class factory { } static transformTSTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { - if (arkts.isETSFunctionType(node.typeAnnotation) && hasDecorator(node.typeAnnotation, DecoratorNames.BUILDER)) { + if ( + node.typeAnnotation && + arkts.isETSFunctionType(node.typeAnnotation) && + hasDecorator(node.typeAnnotation, DecoratorNames.BUILDER) + ) { node.typeAnnotation.setAnnotations([annotation(MemoNames.MEMO)]); - } + } return node; } diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index 7c68e9429..c4687bb04 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -30,22 +30,6 @@ export type CustomComponentScopeInfo = CustomComponentInfo & { hasReusableRebind?: boolean; }; -/** - * Determine whether it is method with specified name. - * - * @param method method definition node - * @param name specified method name - */ -export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { - if (!method || !arkts.isMethodDefinition(method)) { - return false; - } - - // For now, we only considered matched method name. - const isNameMatched: boolean = method.name?.name === name; - return isNameMatched; -} - /** * Determine whether it is ETSGLOBAL class. * diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index c5f735bf9..ad0d3da64 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -17,6 +17,7 @@ import * as arkts from '@koalaui/libarkts'; import { addMemoAnnotation, BuilderLambdaNames, + CustomComponentAnontations, CustomComponentNames, findCanAddMemoFromParamExpression, hasNullOrUndefinedType, @@ -98,18 +99,6 @@ export class factory { ); } - /** - * create `@memo() style: ((instance: ) => void) | undefined` as parameter - */ - static createStyleParameter(typeName: string): arkts.ETSParameterExpression { - const styleParam: arkts.Identifier = factory.createStyleIdentifier(typeName); - const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration(styleParam, undefined); - if (findCanAddMemoFromParamExpression(param)) { - addMemoAnnotation(param); - } - return param; - } - /** * create `initializers: | undefined` as identifier */ @@ -370,4 +359,35 @@ export class factory { } return st; } + + /* + * create class implements : `implements `. + */ + static createClassImplements( + interfaceName: string, + typeParameters?: arkts.TSTypeParameterInstantiation + ): arkts.TSClassImplements { + return arkts.factory.createTSClassImplements( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(interfaceName)) + ), + typeParameters + ); + } + + /** + * Generate class implements for struct with struct annotations. + * + * @param method method definition node + */ + static generateImplementsForStruct(annotations: CustomComponentAnontations): arkts.TSClassImplements[] { + const implementsInfo: arkts.TSClassImplements[] = []; + if (annotations.entry) { + implementsInfo.push(factory.createClassImplements(CustomComponentNames.PAGE_LIFE_CYCLE)); + } + if (annotations.customLayout) { + implementsInfo.push(factory.createClassImplements(CustomComponentNames.LAYOUT_CALLBACK)); + } + return implementsInfo; + } } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index e66bf494d..8a1cc6c61 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -23,13 +23,15 @@ export enum CustomComponentNames { COMPONENT_BUILD_ORI = 'build', COMPONENT_CONSTRUCTOR_ORI = 'constructor', COMPONENT_CLASS_NAME = 'CustomComponent', + COMPONENT_V2_CLASS_NAME = 'CustomComponentV2', COMPONENT_INTERFACE_PREFIX = '__Options_', COMPONENT_INITIALIZE_STRUCT = '__initializeStruct', COMPONENT_UPDATE_STRUCT = '__updateStruct', - COMPONENT_BUILD = '_build', COMPONENT_INITIALIZERS_NAME = 'initializers', BUILDCOMPATIBLENODE = '_buildCompatibleNode', OPTIONS = 'options', + PAGE_LIFE_CYCLE = 'PageLifeCycle', + LAYOUT_CALLBACK = 'LayoutCallback', } export enum BuilderLambdaNames { @@ -137,8 +139,20 @@ export type CustomComponentInfo = { export type CustomComponentAnontations = { component?: arkts.AnnotationUsage; + componentV2?: arkts.AnnotationUsage; entry?: arkts.AnnotationUsage; reusable?: arkts.AnnotationUsage; + reusableV2?: arkts.AnnotationUsage; + customLayout?: arkts.AnnotationUsage; +}; + +type StructAnnoationInfo = { + isComponent: boolean; + isComponentV2: boolean; + isEntry: boolean; + isReusable: boolean; + isReusableV2: boolean; + isCustomLayout: boolean; }; export function isCustomComponentAnnotation( @@ -174,22 +188,28 @@ export function collectCustomComponentScopeInfo( const isDecl: boolean = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); const isCustomComponentClassDecl = !isStruct && isDecl; const shouldIgnoreDecl = isStruct || isDecl; - if (isCustomComponentClassDecl && definition.ident.name !== CustomComponentNames.COMPONENT_CLASS_NAME) { + if ( + isCustomComponentClassDecl && + definition.ident.name !== CustomComponentNames.COMPONENT_CLASS_NAME && + definition.ident.name !== CustomComponentNames.COMPONENT_V2_CLASS_NAME + ) { return undefined; } let annotations: CustomComponentAnontations = {}; if (!isCustomComponentClassDecl) { let isCustomComponent: boolean = false; for (const anno of definition.annotations) { - const isComponent = isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT, shouldIgnoreDecl); - const isEntry = isCustomComponentAnnotation(anno, StructDecoratorNames.ENTRY, shouldIgnoreDecl); - const isReusable = isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE, shouldIgnoreDecl); - isCustomComponent ||= isComponent; + const { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout } = + getAnnotationInfoForStruct(anno, shouldIgnoreDecl); + isCustomComponent ||= isComponent || isComponentV2; annotations = { ...annotations, ...(isComponent && !annotations?.component && { component: anno }), + ...(isComponentV2 && !annotations?.componentV2 && { componentV2: anno }), ...(isEntry && !annotations?.entry && { entry: anno }), ...(isReusable && !annotations?.reusable && { reusable: anno }), + ...(isReusableV2 && !annotations?.reusableV2 && { reusableV2: anno }), + ...(isCustomLayout && !annotations?.customLayout && { customLayout: anno }), }; } if (!isCustomComponent) { @@ -203,6 +223,19 @@ export function collectCustomComponentScopeInfo( }; } +export function getAnnotationInfoForStruct( + anno: arkts.AnnotationUsage, + shouldIgnoreDecl: boolean +): StructAnnoationInfo { + const isComponent = isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT, shouldIgnoreDecl); + const isComponentV2 = isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT_V2, shouldIgnoreDecl); + const isEntry = isCustomComponentAnnotation(anno, StructDecoratorNames.ENTRY, shouldIgnoreDecl); + const isReusable = isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE, shouldIgnoreDecl); + const isReusableV2 = isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE_V2, shouldIgnoreDecl); + const isCustomLayout = isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOM_LAYOUT, shouldIgnoreDecl); + return { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout }; +} + export function isComponentStruct(node: arkts.StructDeclaration, scopeInfo: CustomComponentInfo): boolean { return scopeInfo.name === node.definition.ident?.name; } @@ -218,7 +251,9 @@ export function isCustomComponentClass(node: arkts.ClassDeclaration, scopeInfo: } const name: string = node.definition.ident.name; if (scopeInfo.isDecl) { - return name === CustomComponentNames.COMPONENT_CLASS_NAME; + return ( + name === CustomComponentNames.COMPONENT_CLASS_NAME || name === CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); } return name === scopeInfo.name; } @@ -312,3 +347,19 @@ export function addMemoAnnotation(node: T, memoName: Memo } return node.setAnnotations(newAnnotations) as T; } + +/** + * Determine whether it is method with specified name. + * + * @param method method definition node + * @param name specified method name + */ +export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { + if (!method || !arkts.isMethodDefinition(method)) { + return false; + } + + // For now, we only considered matched method name. + const isNameMatched: boolean = method.name?.name === name; + return isNameMatched; +} diff --git a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts index 0f9c3e283..f9ad24118 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts @@ -77,6 +77,7 @@ import { ArrayExpression, AnnotationDeclaration, TryStatement, + TSClassImplements, } from '../../generated'; import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; @@ -134,6 +135,7 @@ import { updateTemplateLiteral } from '../node-utilities/TemplateLiteral'; import { updateArrayExpression } from '../node-utilities/ArrayExpression'; import { updateAnnotationDeclaration } from '../node-utilities/AnnotationDeclaration'; import { updateTryStatement } from '../node-utilities/TryStatement'; +import { updateTSClassImplements } from '../node-utilities/TSClassImplements'; export const factory = { get createIdentifier(): (...args: Parameters) => Identifier { @@ -565,6 +567,12 @@ export const factory = { get updateTryStatement(): (...args: Parameters) => TryStatement { return updateTryStatement; }, + get createTSClassImplements(): (...args: Parameters) => TSClassImplements { + return TSClassImplements.createTSClassImplements; + }, + get UpdateTSClassImplements(): (...args: Parameters) => TSClassImplements { + return updateTSClassImplements; + }, /** @deprecated */ createTypeParameter1_(name: Identifier, constraint?: TypeNode, defaultType?: TypeNode) { return TSTypeParameter.createTSTypeParameter(Identifier.create1Identifier(name.name), constraint, defaultType); diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts b/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts new file mode 100644 index 000000000..1e51e9c44 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/TSClassImplements.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Expression, TSClassImplements, TSTypeParameterInstantiation } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateTSClassImplements( + original: TSClassImplements, + expression?: Expression, + typeParameters?: TSTypeParameterInstantiation +): TSClassImplements { + if (isSameNativeObject(expression, original.expr) && isSameNativeObject(typeParameters, original.typeParameters)) { + return original; + } + + const update = updateThenAttach(TSClassImplements.updateTSClassImplements, attachModifiers); + return update(original, expression, typeParameters); +} diff --git a/koala-wrapper/src/generated/index.ts b/koala-wrapper/src/generated/index.ts index 0416a0691..81fc18f94 100644 --- a/koala-wrapper/src/generated/index.ts +++ b/koala-wrapper/src/generated/index.ts @@ -189,3 +189,4 @@ export * from "./peers/ETSDynamicFunctionType" export * from "./peers/InterfaceDecl" export * from "./peers/FunctionDecl" export * from "./peers/Context" +export * from "./peers/TSClassImplements"; -- Gitee