* Copyright (c) 2022-2025 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 * as arkts from '@koalaui/libarkts';
import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor';
import {
getCustomComponentOptionsName,
CustomComponentInfo,
CustomComponentAnontations,
collectCustomComponentScopeInfo,
isComponentStruct,
isCustomDialogControllerOptions,
getComponentExtendsName,
ComponentType,
EntryAnnoInfo,
getValueInObjectAnnotation,
} from './utils';
import {
backingField,
expectName,
annotation,
filterDefined,
collect,
isDecoratorAnnotation,
withAPIVersion,
expectNameInTypeReference,
isExported,
} from '../common/arkts-utils';
import { ProjectConfig } from '../common/plugin-context';
import { findNavigationModuleInfo, getEntryRouteParam } from './entry-translators/utils';
import { factory as EntryFactory } from './entry-translators/factory';
import { hasDecoratorName, checkIsRequiredPropertyFromAnnotationInfo, collectAnnotationsFromInfo, collectAnnotationForBackingFromInfo } from './property-translators/utils';
import { factory as UIFactory } from './ui-factory';
import { factory as PropertyFactory } from './property-translators/factory';
import { factory as StructFactory } from './struct-translators/factory';
import {
ARKUI_NAVIGATION_SOURCE_NAME,
ARKUI_NAV_DESTINATION_SOURCE_NAME,
ARKUI_COMPONENT_COMMON_SOURCE_NAME,
CUSTOM_COMPONENT_IMPORT_SOURCE_NAME,
DecoratorNames,
DECORATOR_TYPE_MAP,
NavigationNames,
EntryWrapperNames,
ReuseNames,
CustomComponentNames,
LogType,
ReusableOptions,
APIVersions,
APIComparison,
StateManagementTypes,
ARKUI_STATE_MANAGEMENT_DECORATOR_SOURCE_NAME,
} from '../common/predefines';
import { ImportCollector } from '../common/import-collector';
import { MetaDataCollector } from '../common/metadata-collector';
import {
LogCollector,
createSuggestion,
getPositionRangeFromAnnotation,
getPositionRangeFromNode,
} from '../common/log-collector';
import { NamespaceProcessor } from './namespace-processor';
import { AstNodePointer } from '../common/safe-types';
import { AnnotationRecord } from '../collectors/ui-collectors/records/annotations/base';
import { StructPropertyAnnotationInfo, StructPropertyAnnotations } from '../collectors/ui-collectors/records';
import { parseStructPropertyAnnotations } from '../collectors/ui-collectors/utils';
export interface ComponentTransformerOptions extends VisitorOptions {
arkui?: string;
}
type ScopeInfo = CustomComponentInfo;
export interface InteropContext {
className: string;
path?: string;
line?: number;
col?: number;
arguments?: arkts.ObjectExpression;
content?: arkts.ArrowFunctionExpression;
storage?: arkts.Identifier;
}
export class ComponentTransformer extends AbstractVisitor {
private scopeInfos: ScopeInfo[] = [];
private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = [];
private entryAnnoInfo: EntryAnnoInfo[] = [];
private structMembersMap: Map<string, arkts.AstNode[]> = new Map();
private projectConfig: ProjectConfig | undefined;
private entryRouteName: arkts.Expression | undefined;
private componentType: ComponentType = {
hasEntry: false,
hasComponent: false,
hasComponentV2: false,
hasCustomDialog: false,
hasCustomLayout: false,
hasReusable: false,
hasReusableV2: false
};
private importCollector: ImportCollector;
constructor(options?: ComponentTransformerOptions) {
const _options: ComponentTransformerOptions = options ?? {};
super(_options);
this.projectConfig = MetaDataCollector.getInstance().projectConfig;
this.importCollector = ImportCollector.getInstance();
}
init(): void {
MetaDataCollector.getInstance()
.setProjectConfig(this.projectConfig)
.setAbsName(this.program?.absoluteName)
.setExternalSourceName(this.externalSourceName)
.setIsDeclaration(this.isDeclaration);
}
reset(): void {
super.reset();
this.importCollector.reset();
this.scopeInfos = [];
this.componentInterfaceCollection = [];
this.entryAnnoInfo = [];
NamespaceProcessor.getInstance().reset();
this.structMembersMap = new Map();
this.componentType = {
hasEntry: false,
hasComponent: false,
hasComponentV2: false,
hasCustomDialog: false,
hasCustomLayout: false,
hasReusable: false,
hasReusableV2: false
};
}
enterStruct(node: arkts.ETSStructDeclaration): void {
if (!node.definition.ident) {
return;
}
const info: ScopeInfo | undefined = collectCustomComponentScopeInfo(node);
if (info) {
this.scopeInfos.push(info);
} else {
LogCollector.getInstance().collectLogInfo({
node: node,
message: `Annotation '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '${node.definition.ident.name}'.`,
level: LogType.ERROR,
});
}
}
exitStruct(node: arkts.ETSStructDeclaration | arkts.ClassDeclaration): void {
if (!node.definition || !node.definition.ident || this.scopeInfos.length === 0) {
return;
}
if (this.scopeInfos[this.scopeInfos.length - 1]?.name === node.definition.ident.name) {
this.scopeInfos.pop();
}
}
getUpdateStatements(): arkts.Statement[] {
let updateStatements: arkts.Statement[] = [];
if (this.componentInterfaceCollection.length > 0) {
this.insertComponentImport();
updateStatements.push(...this.componentInterfaceCollection);
}
if (this.entryAnnoInfo.length > 0) {
updateStatements.push(...this.entryAnnoInfo.map(EntryFactory.generateEntryWrapper));
updateStatements.push(
...this.entryAnnoInfo.map((item: EntryAnnoInfo) =>
EntryFactory.callRegisterNamedRouter(
this.entryRouteName,
this.projectConfig,
this.program?.absoluteName,
item.startPosition,
item.endPosition
)
)
);
this.importCollector.collectSource(NavigationNames.NAVINTERFACE, CUSTOM_COMPONENT_IMPORT_SOURCE_NAME);
this.importCollector.collectImport(NavigationNames.NAVINTERFACE);
}
return updateStatements;
}
processRootETSModule(node: arkts.ETSModule): arkts.ETSModule {
if (this.isExternal && this.externalSourceName === CUSTOM_COMPONENT_IMPORT_SOURCE_NAME) {
const navInterface = EntryFactory.createNavInterface();
navInterface.modifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT;
return arkts.factory.updateETSModule(node, [...node.statements, navInterface]);
}
let _newNode: arkts.ETSModule | undefined;
withAPIVersion(
{ version: APIVersions.API_24, compare: APIComparison.LESS_THAN_OR_EQUAL },
(sdkVersion: APIVersions) => {
if (
this.externalSourceName === ARKUI_NAVIGATION_SOURCE_NAME ||
this.externalSourceName === ARKUI_NAV_DESTINATION_SOURCE_NAME
) {
const statements = node.statements;
const foundDecl = findNavigationModuleInfo(statements, this.externalSourceName);
if (!foundDecl) {
_newNode = arkts.factory.updateETSModule(node, [
...node.statements,
EntryFactory.createNavigationModuleInfo(this.externalSourceName),
]);
}
}
},
{ ignoreCompare: true }
);
if (_newNode !== undefined) {
return _newNode;
}
if (
this.isExternal &&
this.entryAnnoInfo.length === 0 &&
NamespaceProcessor.getInstance().totalInnerClassesCnt === 0
) {
return node;
}
const navigationBuilderClass = StructFactory.createNavigationBuilderRegisterClass(
node,
MetaDataCollector.getInstance().routerInfo,
MetaDataCollector.getInstance().fileAbsName
);
this.insertComponentImport();
const updateStatements: arkts.Statement[] = this.getUpdateStatements();
if (updateStatements.length > 0 || this.importCollector.importInfos.length > 0 || navigationBuilderClass !== undefined) {
const newStatements = collect<arkts.Statement>(
this.importCollector.getImportStatements(),
node.statements,
navigationBuilderClass ? [navigationBuilderClass] : [],
updateStatements
);
return arkts.factory.updateETSModule(node, newStatements);
}
return node;
}
private _collectImportFromCustomComponentSource(importName: string): void {
this.importCollector.collectSource(importName, CUSTOM_COMPONENT_IMPORT_SOURCE_NAME);
this.importCollector.collectImport(importName);
}
private _collectImportFromEntrySource(importName: string): void {
this.importCollector.collectSource(importName, CUSTOM_COMPONENT_IMPORT_SOURCE_NAME);
this.importCollector.collectImport(importName);
}
insertComponentImport(): void {
if (this.componentType.hasComponent) {
this._collectImportFromCustomComponentSource(CustomComponentNames.COMPONENT_CLASS_NAME);
this._collectImportFromCustomComponentSource(StateManagementTypes.REUSE_POOL_OWNERSHIP);
}
if (this.componentType.hasComponentV2) {
this._collectImportFromCustomComponentSource(CustomComponentNames.COMPONENT_V2_CLASS_NAME);
this._collectImportFromCustomComponentSource(StateManagementTypes.REUSE_POOL_OWNERSHIP);
}
if (this.componentType.hasCustomDialog) {
this._collectImportFromCustomComponentSource(CustomComponentNames.BASE_CUSTOM_DIALOG_NAME);
}
if (this.componentType.hasCustomLayout) {
this._collectImportFromCustomComponentSource(CustomComponentNames.LAYOUT_CALLBACKS);
}
if (this.componentType.hasReusable || this.componentType.hasReusableV2) {
this._collectImportFromCustomComponentSource(ReusableOptions.REUSABLE_MEM_OPT_STRATEGY);
}
if (this.componentType.hasEntry) {
this._collectImportFromCustomComponentSource(CustomComponentNames.PAGE_LIFE_CYCLE);
}
if (this.entryAnnoInfo.length > 0) {
this._collectImportFromEntrySource(EntryWrapperNames.ENTRY_POINT_CLASS_NAME);
}
}
updateEntryPoint(node: arkts.ClassDeclaration): arkts.ClassDeclaration {
if (!node.definition) {
return node;
}
return arkts.factory.updateClassDeclaration(
node,
arkts.factory.updateClassDefinition(
node.definition,
node.definition.ident,
node.definition.typeParams,
node.definition.superTypeParams,
node.definition.implements,
undefined,
node.definition.super,
[
EntryFactory.generateRegisterNamedRouter(),
EntryFactory.generateNavigationBuilderRegister(),
...node.definition.body,
],
node.definition.modifiers,
arkts.classDefinitionFlags(node.definition),
node.definition.annotations
)
);
}
createStaticMethod(definition: arkts.ClassDefinition): arkts.MethodDefinition {
const isDecl: boolean = MetaDataCollector.getInstance().isDeclaration ||
arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE);
let modifiers =
arkts.classDefinitionFlags(definition) |
arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC |
arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC;
if (isDecl && !arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE)) {
modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE;
}
const body = isDecl ? undefined : arkts.factory.createBlockStatement([arkts.factory.createReturnStatement()]);
const param: arkts.ETSParameterExpression = arkts.factory.createETSParameterExpression(
arkts.factory.createIdentifier(
CustomComponentNames.OPTIONS,
UIFactory.createTypeReferenceFromString(getCustomComponentOptionsName(definition.ident!.name))
),
false
);
const returnTypeAnnotation = arkts.factory.createETSPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID);
const flags = arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD;
const kind = arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD;
const key = arkts.factory.createIdentifier(CustomComponentNames.BUILDCOMPATIBLENODE);
return UIFactory.createMethodDefinition({
key: key.clone(),
kind,
function: {
key,
body,
params: [param],
returnTypeAnnotation,
flags,
modifiers,
},
modifiers,
});
}
processComponent(
node: arkts.ClassDeclaration | arkts.ETSStructDeclaration
): arkts.ClassDeclaration | arkts.ETSStructDeclaration {
const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1];
const className = node.definition?.ident?.name;
if (!className || scopeInfo?.name !== className) {
return node;
}
const structPropAnnoMap = new Map();
const definition: arkts.ClassDefinition = node.definition!;
if (arkts.isETSStructDeclaration(node)) {
if (definition.super || definition.implements.length !== 0) {
LogCollector.getInstance().collectLogInfo({
node: definition?.ident ?? node,
message: `Structs are not allowed to inherit from classes or implement interfaces.`,
level: LogType.ERROR,
});
return node;
}
this.validateBuildMethod(node, className);
const innerClassFields: arkts.AstNode[] = [];
definition.body.forEach((it) => {
if (!arkts.isClassProperty(it)) {
return;
}
const structPropAnnoRecord = parseStructPropertyAnnotations(it, true);
structPropAnnoMap.set(it.peer, structPropAnnoRecord);
innerClassFields.push(...this.createInterfaceInnerMember(it, structPropAnnoRecord));
})
this.structMembersMap.set(className, innerClassFields);
}
const customComponentInnerClass = this.generateComponentInnerClass(
className,
StructFactory.copyStructModifierFlagsToOptionsInnerClass(node.modifierFlags, isExported(this.program, node)),
Object.values(scopeInfo.annotations ?? {}).map((anno) => anno.clone()),
scopeInfo.isDecl
);
NamespaceProcessor.getInstance().addInnerClassToCurrentNamespace(customComponentInnerClass);
if (!MetaDataCollector.getInstance().isDeclaration && !!scopeInfo.annotations?.entry) {
this.validateEntryParams(scopeInfo.annotations);
this.entryAnnoInfo.push({
name: className,
startPosition: scopeInfo.annotations.entry.startPosition,
endPosition: scopeInfo.annotations.entry.endPosition,
annotation: scopeInfo.annotations.entry,
});
const routeName = getEntryRouteParam(definition);
if (routeName && routeName.value) {
this.entryRouteName = routeName.value;
}
}
const newDefinition: arkts.ClassDefinition = this.createNewDefinition(
node,
className,
definition,
structPropAnnoMap
);
if (arkts.isETSStructDeclaration(node)) {
newDefinition.setFromStructModifier()
const _node = arkts.factory.createClassDeclaration(newDefinition, node.modifierFlags);
_node.startPosition = node.startPosition;
_node.endPosition = node.endPosition;
return _node;
} else {
return arkts.factory.updateClassDeclaration(node, newDefinition);
}
}
private validateBuildMethod(node: arkts.ETSStructDeclaration, className: string): void {
const buildMethods = node.definition!.body.filter((member): member is arkts.MethodDefinition => {
if (!arkts.isMethodDefinition(member)) {
return false;
}
return !!member.id && arkts.isIdentifier(member.id) && member.id.name === 'build';
});
if (buildMethods.length === 0) {
const position = arkts.createSourcePosition(node.endPosition.getIndex() - 1, node.endPosition.getLine());
LogCollector.getInstance().collectLogInfo({
node: node.definition?.ident ?? node,
message: `The struct '${className}' must have at least and at most one 'build' method.`,
level: LogType.ERROR,
suggestions: [createSuggestion('build() {\n}\n', position, position, `Add a build function to the custom component`)],
});
return;
}
if (buildMethods.length > 1) {
for (const buildMethod of buildMethods) {
LogCollector.getInstance().collectLogInfo({
node: buildMethod.id ?? buildMethod,
message: `The struct '${className}' must have at least and at most one 'build' method.`,
level: LogType.ERROR,
suggestions: [createSuggestion('', buildMethod.startPosition, buildMethod.endPosition, `Remove the duplicate build function`)],
});
}
return;
}
const buildMethod = buildMethods[0];
const params = buildMethod.function?.params ?? [];
if (params.length > 0) {
const firstParam = params[0];
const lastParam = params[params.length - 1];
for (const param of params) {
LogCollector.getInstance().collectLogInfo({
node: param,
message: `The 'build' method can not have arguments.`,
level: LogType.ERROR,
suggestions: [createSuggestion('', firstParam.startPosition, lastParam.endPosition, `Remove the parameters of the build function`)],
});
}
}
}
private validateEntryParams(annotations: CustomComponentAnontations): void {
const entryAnnotation = annotations.entry;
const componentV2Annotation = annotations.componentV2;
if (!entryAnnotation || !componentV2Annotation) {
return;
}
const hasInvalidParam = entryAnnotation.properties.some(
(property) =>
arkts.isClassProperty(property) &&
property.key &&
arkts.isIdentifier(property.key) &&
(property.key.name === 'storage' || property.key.name === 'useSharedStorage')
);
if (!hasInvalidParam) {
return;
}
LogCollector.getInstance().collectLogInfo({
node: entryAnnotation,
message: `The "@Entry" decorator that has "storage" or "useSharedStorage" parameters cannot be used together with the "@ComponentV2" decorator.`,
level: LogType.ERROR,
});
}
getSystemEnvKeyType(envKeyClass: string): string | undefined {
if (envKeyClass === StateManagementTypes.WRITABLE_ENV_KEY) {
return StateManagementTypes.WRITABLE_SYSTEM_ENV_KEY;
}
if (envKeyClass === StateManagementTypes.READONLY_ENV_KEY) {
return StateManagementTypes.READONLY_SYSTEM_ENV_KEY;
}
return undefined;
}
generateEnvTypeCheckStatements(
definition: arkts.ClassDefinition
): arkts.Statement[] {
const statements: arkts.Statement[] = [];
for (const member of definition.body) {
if (!arkts.isClassProperty(member) || !hasDecoratorName(member, DecoratorNames.ENV)) {
continue;
}
const propertyName: string = expectName(member.key);
const typeAnnotation = member.typeAnnotation;
let envValueExpr: arkts.Expression | undefined;
for (const anno of member.annotations) {
if (anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.ENV) {
envValueExpr = getValueInObjectAnnotation(anno, DecoratorNames.ENV, 'value');
break;
}
}
if (!envValueExpr || !arkts.isStringLiteral(envValueExpr)) {
continue;
}
const envPattern = /^(WritableEnvKey|ReadonlyEnvKey)\.(\w+)$/;
const match = envPattern.exec(envValueExpr.str);
if (!match) {
continue;
}
const envKeyClass = match[1];
const envKeyMember = match[2];
const systemEnvKeyType = this.getSystemEnvKeyType(envKeyClass);
if (!systemEnvKeyType) {
continue;
}
this.importCollector.collectSource(envKeyClass, ARKUI_STATE_MANAGEMENT_DECORATOR_SOURCE_NAME);
this.importCollector.collectImport(envKeyClass);
this.importCollector.collectSource(systemEnvKeyType, ARKUI_STATE_MANAGEMENT_DECORATOR_SOURCE_NAME);
this.importCollector.collectImport(systemEnvKeyType);
const memberExpr = arkts.factory.createMemberExpression(
arkts.factory.createIdentifier(envKeyClass),
arkts.factory.createIdentifier(envKeyMember),
arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS,
false,
false
);
if (envValueExpr.startPosition && envValueExpr.endPosition) {
memberExpr.startPosition = envValueExpr.startPosition;
memberExpr.endPosition = envValueExpr.endPosition;
}
const systemEnvKeyRef = arkts.factory.createETSTypeReference(
arkts.factory.createETSTypeReferencePart(
arkts.factory.createIdentifier(systemEnvKeyType),
typeAnnotation
? arkts.factory.createTSTypeParameterInstantiation([typeAnnotation.clone()])
: undefined
)
);
const varDeclarator = arkts.factory.createVariableDeclarator(
arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST,
arkts.factory.createIdentifier(`__env_${propertyName}`, systemEnvKeyRef),
memberExpr
);
const varDecl = arkts.factory.createVariableDeclaration(
arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST,
[varDeclarator]
);
if (envValueExpr.startPosition && envValueExpr.endPosition) {
varDecl.startPosition = envValueExpr.startPosition;
varDecl.endPosition = envValueExpr.endPosition;
}
statements.push(varDecl);
}
return statements;
}
generateCustomEnvTypeCheckStatements(
definition: arkts.ClassDefinition
): arkts.Statement[] {
const statements: arkts.Statement[] = [];
for (const member of definition.body) {
if (!arkts.isClassProperty(member) || !hasDecoratorName(member, DecoratorNames.CUSTOM_ENV)) {
continue;
}
const propertyName: string = expectName(member.key);
const typeAnnotation = member.typeAnnotation;
let envKeyExpr: arkts.Expression | undefined;
for (const anno of member.annotations) {
if (anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.CUSTOM_ENV) {
envKeyExpr = getValueInObjectAnnotation(anno, DecoratorNames.CUSTOM_ENV, 'value');
break;
}
}
if (!envKeyExpr || !arkts.isStringLiteral(envKeyExpr)) {
continue;
}
const envKeyId = arkts.factory.createIdentifier(envKeyExpr.str);
if (envKeyExpr.startPosition && envKeyExpr.endPosition) {
envKeyId.startPosition = envKeyExpr.startPosition;
envKeyId.endPosition = envKeyExpr.endPosition;
}
const customEnvKeyType = arkts.factory.createETSTypeReference(
arkts.factory.createETSTypeReferencePart(
arkts.factory.createIdentifier(StateManagementTypes.CUSTOM_ENV_KEY),
typeAnnotation
? arkts.factory.createTSTypeParameterInstantiation([typeAnnotation.clone()])
: undefined
)
);
const varDeclarator = arkts.factory.createVariableDeclarator(
arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST,
arkts.factory.createIdentifier(`__customEnv_${propertyName}`, customEnvKeyType),
envKeyId
);
const varDecl = arkts.factory.createVariableDeclaration(
arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST,
[varDeclarator]
);
if (envKeyExpr.startPosition && envKeyExpr.endPosition) {
varDecl.startPosition = envKeyExpr.startPosition;
varDecl.endPosition = envKeyExpr.endPosition;
}
statements.push(varDecl);
}
return statements;
}
generateResolveDecoratorSymbolsMethod(
definition: arkts.ClassDefinition
): arkts.MethodDefinition | undefined {
const bodyStatements: arkts.Statement[] = [
...this.generateEnvTypeCheckStatements(definition),
...this.generateCustomEnvTypeCheckStatements(definition),
];
if (bodyStatements.length === 0) {
return undefined;
}
const body = arkts.factory.createBlockStatement(bodyStatements);
const returnTypeAnnotation = arkts.factory.createETSPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID);
const key = arkts.factory.createIdentifier(CustomComponentNames.RESOLVE_DECORATOR_SYMBOLS_METHOD);
const modifiers =
arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC |
arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC;
const scriptFunction = arkts.factory.createScriptFunction(
body,
undefined,
[],
returnTypeAnnotation,
false,
arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD,
arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE,
key,
undefined
);
return arkts.factory.createMethodDefinition(
arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD,
key.clone(),
arkts.factory.createFunctionExpression(key.clone(), scriptFunction),
modifiers,
false
);
}
createNewDefinition(
node: arkts.ClassDeclaration | arkts.ETSStructDeclaration,
className: string,
definition: arkts.ClassDefinition,
structPropAnnoMap: Map<AstNodePointer, AnnotationRecord<StructPropertyAnnotations, StructPropertyAnnotationInfo> | undefined>
): arkts.ClassDefinition {
const staticMethodBody: arkts.AstNode[] = [];
const isExportClass = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT);
if (isExportClass) {
const buildCompatibleNode: arkts.MethodDefinition = this.createStaticMethod(definition);
if (!!buildCompatibleNode) {
staticMethodBody.push(buildCompatibleNode);
}
}
const resolveMethod = this.generateResolveDecoratorSymbolsMethod(definition);
if (resolveMethod) {
staticMethodBody.push(resolveMethod);
}
const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1];
const isDecl = scopeInfo.isDecl;
const extendsName: string = getComponentExtendsName(scopeInfo.annotations, this.componentType);
return arkts.factory
.createClassDefinition(
definition.ident,
undefined,
undefined,
[...definition.implements, ...UIFactory.generateImplementsForStruct(scopeInfo.annotations)],
undefined,
arkts.factory.createETSTypeReference(
arkts.factory.createETSTypeReferencePart(
arkts.factory.createIdentifier(extendsName),
arkts.factory.createTSTypeParameterInstantiation([
UIFactory.createTypeReferenceFromString(className),
UIFactory.createTypeReferenceFromString(
`${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`
),
])
)
),
[
StructFactory.createInvokeMethod(className, isDecl),
...definition.body.map((st: arkts.AstNode) => {
if (!scopeInfo.isDecl && arkts.isClassProperty(st)) {
UIFactory.preprocessClassPropertyModifier(st, structPropAnnoMap.get(st.peer));
}
return StructFactory.updateStructConstructor(st, scopeInfo);
}),
...staticMethodBody,
],
definition.modifiers,
arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL,
definition.annotations
);
}
generateComponentInnerClass(
name: string,
modifiers: arkts.Es2pandaModifierFlags,
annotations?: readonly arkts.AnnotationUsage[],
isDecl?: boolean
): arkts.ClassDeclaration {
const ctor = EntryFactory.generateConstructor(isDecl);
const definition: arkts.ClassDefinition = arkts.factory
.createClassDefinition(
arkts.factory.createIdentifier(getCustomComponentOptionsName(name)),
undefined,
undefined,
[],
undefined,
undefined,
[...(this.structMembersMap.get(name) || []), ctor],
arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_CLASS_DECL |
arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_DECLARATION |
arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_ID_REQUIRED,
modifiers
)
.setCtor(ctor)
.setAnnotations(annotations ?? []);
const newClass = arkts.factory.createClassDeclaration(definition);
newClass.modifierFlags = modifiers;
return newClass;
}
createInterfaceInnerMember(
member: arkts.ClassProperty,
annotationRecord: AnnotationRecord<StructPropertyAnnotations, StructPropertyAnnotationInfo> | undefined
): arkts.AstNode[] {
const annotations: arkts.AnnotationUsage[] = collectAnnotationsFromInfo(annotationRecord);
const isRequired: boolean = checkIsRequiredPropertyFromAnnotationInfo(annotationRecord);
const originalName: string = expectName(member.key);
const originalMember: arkts.ClassProperty = PropertyFactory.createOptionalClassProperty({
name: originalName,
propertyType: member.typeAnnotation?.clone(),
modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC,
isRequired
})
.setAnnotations(annotations);
const optionsHasMember = UIFactory.createOptionsHasMember(originalName);
const typedAnnotations = collectAnnotationForBackingFromInfo(annotationRecord);
if (typedAnnotations.length > 0) {
const newName: string = backingField(originalName);
const newMember: arkts.ClassProperty = PropertyFactory
.createOptionalClassProperty({
name: newName,
propertyType: member.typeAnnotation?.clone(),
modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC
})
.setAnnotations(typedAnnotations);
return [originalMember, newMember, optionsHasMember];
}
return [originalMember, optionsHasMember];
}
visitStruct(node: arkts.ETSStructDeclaration): arkts.ETSStructDeclaration | arkts.ClassDeclaration {
this.enterStruct(node);
if (this.scopeInfos.length > 0 && isComponentStruct(node, this.scopeInfos[this.scopeInfos.length - 1])) {
const updateNode = this.processComponent(node);
this.exitStruct(updateNode);
return updateNode;
}
return node;
}
private checkDuplicateEntry(): void {
if (this.entryAnnoInfo.length <= 1) {
return;
}
for (const info of this.entryAnnoInfo) {
const annotation = info.annotation;
if (!annotation) {
continue;
}
LogCollector.getInstance().collectLogInfo({
node: annotation,
message: `A page can't contain more than one '@Entry' annotation.`,
level: LogType.ERROR,
suggestions: [createSuggestion('', ...getPositionRangeFromAnnotation(annotation), `Remove the duplicate '@Entry' annotation.`)],
});
}
}
visitETSModule(node: arkts.ETSModule): arkts.ETSModule {
NamespaceProcessor.getInstance().enter();
const isNamespace = node.isNamespace;
const newStatements = node.statements.map((st) => {
if (arkts.isETSStructDeclaration(st)) {
return this.visitStruct(st);
}
if (arkts.isETSModule(st)) {
return this.visitETSModule(st);
}
if (!isNamespace) {
if (
arkts.isClassDeclaration(st) &&
this.externalSourceName === CUSTOM_COMPONENT_IMPORT_SOURCE_NAME &&
st.definition?.ident?.name === EntryWrapperNames.ENTRY_POINT_CLASS_NAME
) {
return this.updateEntryPoint(st);
}
let _newSt: arkts.Statement | undefined;
withAPIVersion(
{ version: APIVersions.API_24, compare: APIComparison.LESS_THAN_OR_EQUAL },
(sdkVersion: APIVersions) => {
if (
arkts.isTSInterfaceDeclaration(st) &&
isCustomDialogControllerOptions(st, this.externalSourceName)
) {
_newSt = UIFactory.updateCustomDialogOptionsInterface(st);
}
},
{ ignoreCompare: true }
);
if (_newSt !== undefined) {
return _newSt;
}
withAPIVersion(
{ version: APIVersions.API_24, compare: APIComparison.GREATER_THAN },
(sdkVersion: APIVersions) => {
if (
arkts.isFunctionDeclaration(st)
&& arkts.hasModifierFlag(st, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE)
&& hasDecoratorName(st, DecoratorNames.BUILDER)
) {
const functionName = st.function!.id?.name;
const returnType = st.function!.returnTypeAnnotation;
const returnTypeName = expectNameInTypeReference(returnType);
if (!!functionName && !!returnTypeName && returnTypeName.name === `${functionName}Attribute`) {
st.function!.setReturnTypeAnnotation(
arkts.factory.createETSPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID)
);
}
}
}
);
}
return st;
});
let newNode = arkts.factory.updateETSModule(node, newStatements);
if (isNamespace) {
newNode = NamespaceProcessor.getInstance().updateCurrentNamespace(newNode);
} else {
newNode = NamespaceProcessor.getInstance().updateCurrentNamespace(this.processRootETSModule(newNode));
}
NamespaceProcessor.getInstance().exit();
return newNode;
}
visitor(node: arkts.AstNode): arkts.AstNode {
if (!arkts.isETSModule(node)) {
return node;
}
const newNode = this.visitETSModule(node);
this.checkDuplicateEntry();
LogCollector.getInstance().emitLogInfo();
LogCollector.getInstance().reset();
return newNode;
}
}