diff --git a/arkui-plugins/BUILD.gn b/arkui-plugins/BUILD.gn index ef13162864136c57f0fb51401eef178cc116ed92..707355e1d2cf1ab6080c64b7799320b8f742e4c9 100755 --- a/arkui-plugins/BUILD.gn +++ b/arkui-plugins/BUILD.gn @@ -33,8 +33,25 @@ action("gen_ui_plugins") { outputs = [ "$target_gen_dir" ] } +action("build_ets_sysResource") { + deps = [":gen_ui_plugins"] + script = "//developtools/ace_ets2bundle/generateSysResource.py" + ets_sysResource = "$target_gen_dir" + "/lib/ui-plugins/sysResource.js" + outputs = [ ets_sysResource ] + + _id_defined_json = "//base/global/system_resources/systemres/main/resources/base/element/id_defined.json" + inputs = [ _id_defined_json ] + + args = [ + "--input-json", + rebase_path(_id_defined_json, root_build_dir), + "--output-js", + rebase_path(ets_sysResource, root_build_dir), + ] +} + ohos_copy("ui_plugin") { - deps = [ ":gen_ui_plugins" ] + deps = [":gen_ui_plugins", ":build_ets_sysResource" ] sources = [ rebase_path("$target_gen_dir") ] outputs = [ target_out_dir + "/$target_name" ] module_source_dir = target_out_dir + "/$target_name" @@ -44,7 +61,7 @@ ohos_copy("ui_plugin") { } ohos_copy("ohos_ets_ui_plugins") { - deps = [ ":gen_ui_plugins" ] + deps = [ ":gen_ui_plugins", ":build_ets_sysResource" ] sources = [ rebase_path("$target_gen_dir") ] outputs = [ ohos_ets_ui_plugins_path ] subsystem_name = "developtools" diff --git a/arkui-plugins/collectors/memo-collectors/factory.ts b/arkui-plugins/collectors/memo-collectors/factory.ts new file mode 100644 index 0000000000000000000000000000000000000000..66d1dcede9919fb9535cd2139a26d9fc240589f3 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/factory.ts @@ -0,0 +1,218 @@ +/* + * Copyright (c) 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 { + addMemoAnnotation, + collectMemoFromCallExpression, + collectMemoFromNewClass, + findCanAddMemoFromArrowFunction, + findCanAddMemoFromClassProperty, + findCanAddMemoFromMethod, + findCanAddMemoFromParameter, + findCanAddMemoFromProperty, + findCanAddMemoFromTypeAlias, +} from './utils'; +import { coerceToAstNode } from '../../common/arkts-utils'; + +export type RewriteAfterFoundFn = ( + node: T, + nodeType: arkts.Es2pandaAstNodeType +) => T; + +export function findAndCollectMemoableNode(node: arkts.AstNode, rewriteFn?: RewriteAfterFoundFn): arkts.AstNode { + const type = arkts.nodeType(node); + if (collectByType.has(type)) { + return collectByType.get(type)!(node, rewriteFn); + } + return node; +} + +export class factory { + /** + * Find and collect possible `@memo` property with arrow function value. + * + * @param node `arkts.Property` node + * @param rewriteFn function callback to rewrite node when it is `@memo` property + * @returns `arkts.Property` node + */ + static findAndCollectMemoableProperty( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromProperty(node)) { + found = true; + addMemoAnnotation(node.value! as arkts.ArrowFunctionExpression); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY); + } + return node; + } + + /** + * Find and collect possible `@memo` class property with arrow function value. + * + * @param node `arkts.ClassProperty` node + * @param rewriteFn function callback to rewrite node when it is `@memo` class property + * @returns `arkts.ClassProperty` node + */ + static findAndCollectMemoableClassProperty( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromClassProperty(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY); + } + return node; + } + + /** + * Find and collect possible `@memo` type alias with function type. + * + * @param node `arkts.TSTypeAliasDeclaration` node + * @param rewriteFn function callback to rewrite node when it is `@memo` type alias + * @returns `arkts.TSTypeAliasDeclaration` node + */ + static findAndCollectMemoableTypeAlias( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromTypeAlias(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION); + } + return node; + } + + /** + * Find and collect possible `@memo` parameter with function type. + * + * @param node `arkts.ETSParameterExpression` node + * @param rewriteFn function callback to rewrite node when it is `@memo` parameter + * @returns `arkts.ETSParameterExpression` node + */ + static findAndCollectMemoableParameter( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromParameter(node)) { + found = true; + addMemoAnnotation(node); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION); + } + return node; + } + + /** + * Find and collect possible `@memo` method. + * + * @param node `arkts.MethodDefinition` node + * @param rewriteFn function callback to rewrite node when it is `@memo` method + * @returns `arkts.MethodDefinition` node + */ + static findAndCollectMemoableMethod( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromMethod(node)) { + found = true; + addMemoAnnotation(node.scriptFunction); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION); + } + return node; + } + + /** + * Find and collect possible `@memo` arrow function. + * + * @param node `arkts.ArrowFunctionExpression` node + * @param rewriteFn function callback to rewrite node when it is `@memo` arrow function + * @returns `arkts.ArrowFunctionExpression` node + */ + static findAndCollectMemoableArrowFunction( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + let found: boolean = false; + if (findCanAddMemoFromArrowFunction(node)) { + found = true; + addMemoAnnotation(node.scriptFunction); + } + if (found && !!rewriteFn) { + return rewriteFn(node, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION); + } + return node; + } + + /** + * Find and collect possible `@memo` function call. + * + * @param node `arkts.CallExpression` node + * @returns `arkts.CallExpression` node + */ + static findAndCollectMemoableCallExpression( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + const _node = coerceToAstNode(node); + collectMemoFromCallExpression(_node); + return node; + } + + /** + * Find and collect new class instance with possible `@memo` type parameters. + * + * @param node `arkts.ETSNewClassInstanceExpression` node + * @returns `arkts.ETSNewClassInstanceExpression` node + */ + static findAndCollectMemoableNewClass( + node: T, + rewriteFn?: RewriteAfterFoundFn + ): T { + const _node = coerceToAstNode(node); + collectMemoFromNewClass(_node); + return node; + } +} + +type CollectFactoryFn = (node: T, rewriteFn?: RewriteAfterFoundFn) => T; + +const collectByType = new Map([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, factory.findAndCollectMemoableProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, factory.findAndCollectMemoableClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, factory.findAndCollectMemoableTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, factory.findAndCollectMemoableParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, factory.findAndCollectMemoableMethod], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, factory.findAndCollectMemoableArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, factory.findAndCollectMemoableCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION, factory.findAndCollectMemoableNewClass], +]); diff --git a/arkui-plugins/collectors/memo-collectors/function-collector.ts b/arkui-plugins/collectors/memo-collectors/function-collector.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f9d07b8ce41349e4bfa04de26dee4904c038fad --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/function-collector.ts @@ -0,0 +1,226 @@ +/* + * Copyright (c) 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 } from '../../common/abstract-visitor'; +import { + checkIsMemoFromMemoableInfo, + collectMemoableInfoInFunctionReturnType, + collectMemoableInfoInScriptFunction, + collectMemoableInfoInVariableDeclarator, + collectMemoableInfoMapInFunctionParams, + collectMemoScriptFunctionBody, + collectScriptFunctionReturnTypeFromInfo, + findIdentifierFromCallee, + getDeclResolveAlias, + MemoableInfo, +} from './utils'; + +export class MemoFunctionCollector extends AbstractVisitor { + private returnMemoableInfo: MemoableInfo | undefined; + private paramMemoableInfoMap: Map | undefined; + private _disableCollectReturn: boolean = false; + private _shouldCollectReturn: boolean = true; + + private get shouldCollectReturn(): boolean { + if (this._disableCollectReturn) { + return false; + } + return this._shouldCollectReturn; + } + + private set shouldCollectReturn(newValue: boolean) { + if (this._disableCollectReturn) { + return; + } + this._shouldCollectReturn = newValue; + } + + private disableCollectReturnBeforeCallback(callbackFn: () => void): void { + const tempValue = this.shouldCollectReturn; + this.shouldCollectReturn = false; + callbackFn(); + this.shouldCollectReturn = tempValue; + } + + private collectMemoAstNode(node: arkts.AstNode, info: MemoableInfo): void { + if (checkIsMemoFromMemoableInfo(info, false)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredPeerInParamMap(node: arkts.CallExpression, peer: arkts.AstNode['peer']): void { + const memoableInfo = this.paramMemoableInfoMap!.get(peer)!; + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredIdInVariableDeclarator( + node: arkts.CallExpression, + declarator: arkts.VariableDeclarator + ): void { + const shouldCollect = + arkts.NodeCache.getInstance().has(declarator) || + (!!declarator.initializer && arkts.NodeCache.getInstance().has(declarator.initializer)); + if (shouldCollect) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private visitVariableDeclarator(node: arkts.VariableDeclarator): arkts.AstNode { + let memoableInfo: MemoableInfo; + if (this.paramMemoableInfoMap?.has(node.name.peer)) { + memoableInfo = this.paramMemoableInfoMap.get(node.name.peer)!; + } else { + memoableInfo = collectMemoableInfoInVariableDeclarator(node); + } + this.collectMemoAstNode(node, memoableInfo); + if (!node.initializer) { + return node; + } + if (arkts.isArrowFunctionExpression(node.initializer)) { + const func = node.initializer.scriptFunction; + const localInfo = collectMemoableInfoInScriptFunction(func); + const shouldCollectParameter = + (localInfo.hasBuilder || localInfo.hasMemo) && !localInfo.hasMemoEntry && !localInfo.hasMemoIntrinsic; + const shouldCollectReturn = + localInfo.hasBuilder || localInfo.hasMemo || memoableInfo.hasBuilder || memoableInfo.hasMemo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + shouldCollectParameter + ); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + !shouldCollectReturn + ); + } + return node; + } + this.shouldCollectReturn = !!memoableInfo.hasMemo || !!memoableInfo.hasBuilder; + this.visitor(node.initializer); + return node; + } + + private visitCallExpression(node: arkts.CallExpression): arkts.AstNode { + if (arkts.NodeCache.getInstance().has(node)) { + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.peer); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.identifier.peer); + } else if (arkts.isIdentifier(decl) && !!decl.parent && arkts.isVariableDeclarator(decl.parent)) { + this.collectCallWithDeclaredIdInVariableDeclarator(node, decl.parent); + } + this.disableCollectReturnBeforeCallback(() => { + this.visitEachChild(node); + }); + return node; + } + + private visitIdentifier(node: arkts.Identifier): arkts.AstNode { + const decl = getDeclResolveAlias(node); + if (!decl) { + return node; + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + arkts.NodeCache.getInstance().collect(node); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + arkts.NodeCache.getInstance().collect(node); + } + return node; + } + + private visitReturnStatement(node: arkts.ReturnStatement): arkts.AstNode { + if (!!this.returnMemoableInfo && !!node.argument && arkts.isArrowFunctionExpression(node.argument)) { + this.collectMemoAstNode(node.argument, this.returnMemoableInfo); + } + arkts.NodeCache.getInstance().collect(node); + this.visitEachChild(node); + return node; + } + + registerReturnInfo(info: MemoableInfo): this { + this.returnMemoableInfo = info; + return this; + } + + registerParamInfoMap(infoMap: Map): this { + this.paramMemoableInfoMap = infoMap; + return this; + } + + disableCollectReturn(): this { + this._disableCollectReturn = true; + return this; + } + + enableCollectReturn(): this { + this._disableCollectReturn = false; + return this; + } + + reset(): void { + this.returnMemoableInfo = undefined; + this.paramMemoableInfoMap = undefined; + this._shouldCollectReturn = true; + this._disableCollectReturn = false; + } + + visitor(node: arkts.AstNode): arkts.AstNode { + if (arkts.isVariableDeclarator(node)) { + return this.visitVariableDeclarator(node); + } + if (arkts.isCallExpression(node)) { + return this.visitCallExpression(node); + } + if (!!this.paramMemoableInfoMap && arkts.isIdentifier(node)) { + return this.visitIdentifier(node); + } + if (arkts.isReturnStatement(node) && this.shouldCollectReturn) { + return this.visitReturnStatement(node); + } + if ( + arkts.isArrowFunctionExpression(node) && + !arkts.NodeCache.getInstance().has(node) && + !arkts.NodeCache.getInstance().has(node.scriptFunction) + ) { + this.shouldCollectReturn = false; + } + return this.visitEachChild(node); + } +} diff --git a/arkui-plugins/collectors/memo-collectors/memo-visitor.ts b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..233288748cbc9a087514119c0bedda40679fb7d3 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 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 } from '../../common/abstract-visitor'; +import { findAndCollectMemoableNode } from './factory'; + +export class MemoVisitor extends AbstractVisitor { + visitor(node: arkts.AstNode): arkts.AstNode { + const newNode = this.visitEachChild(node); + findAndCollectMemoableNode(newNode); + return newNode; + } +} diff --git a/arkui-plugins/collectors/memo-collectors/utils.ts b/arkui-plugins/collectors/memo-collectors/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..61eaeda417de846e730b8aa6f7d610dac530fa4e --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/utils.ts @@ -0,0 +1,913 @@ +/* + * Copyright (c) 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 { annotation, forEachArgWithParam, isDecoratorAnnotation } from '../../common/arkts-utils'; +import { ImportCollector } from '../../common/import-collector'; +import { DecoratorNames, GenSymPrefix, MEMO_IMPORT_SOURCE_NAME } from '../../common/predefines'; +import { MemoFunctionCollector } from './function-collector'; + +export enum MemoNames { + MEMO = 'memo', + MEMO_SKIP = 'memo_skip', + MEMO_INTRINSIC = 'memo_intrinsic', + MEMO_ENTRY = 'memo_entry', +} + +export type MemoAstNode = + | arkts.ScriptFunction + | arkts.ETSParameterExpression + | arkts.ClassProperty + | arkts.TSTypeAliasDeclaration + | arkts.ETSFunctionType + | arkts.ArrowFunctionExpression + | arkts.ETSUnionType + | arkts.VariableDeclaration; + +interface MemoableAnnotationInfo { + hasMemo?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; + hasBuilder?: boolean; + hasBuilderParam?: boolean; +} + +export type MemoableInfo = MemoableAnnotationInfo & { + hasProperType?: boolean; + isWithinTypeParams?: boolean; +}; + +export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: MemoNames): boolean { + return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName; +} + +export function hasMemoAnnotation(node: T): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, MemoNames.MEMO)); +} + +export function addMemoAnnotation(node: T, memoName: MemoNames = MemoNames.MEMO): T { + collectMemoAnnotationSource(memoName); + if (arkts.isETSUnionType(node)) { + return arkts.factory.updateUnionType( + node, + node.types.map((type) => { + if (arkts.isETSFunctionType(type)) { + return addMemoAnnotation(type, memoName); + } + return type; + }) + ) as T; + } + const newAnnotations: arkts.AnnotationUsage[] = [ + ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), + annotation(memoName), + ]; + collectMemoAnnotationImport(memoName); + if (arkts.isEtsParameterExpression(node)) { + node.annotations = newAnnotations; + arkts.NodeCache.getInstance().collect(node); + return node; + } + const newNode = node.setAnnotations(newAnnotations) as T; + arkts.NodeCache.getInstance().collect(newNode); + return newNode; +} + +export function hasMemoableAnnotation(node: T): MemoableAnnotationInfo { + let hasBuilder: boolean = false; + let hasBuilderParam: boolean = false; + let hasMemo: boolean = false; + let hasMemoSkip: boolean = false; + let hasMemoIntrinsic: boolean = false; + let hasMemoEntry: boolean = false; + node.annotations.forEach((it) => { + hasBuilder ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER); + hasBuilderParam ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER_PARAM); + hasMemo ||= isMemoAnnotation(it, MemoNames.MEMO); + hasMemoSkip ||= isMemoAnnotation(it, MemoNames.MEMO_SKIP); + hasMemoIntrinsic ||= isMemoAnnotation(it, MemoNames.MEMO_INTRINSIC); + hasMemoEntry ||= isMemoAnnotation(it, MemoNames.MEMO_ENTRY); + }); + return { + ...(hasMemo ? { hasMemo } : {}), + ...(hasMemoSkip ? { hasMemoSkip } : {}), + ...(hasMemoIntrinsic ? { hasMemoIntrinsic } : {}), + ...(hasMemoEntry ? { hasMemoEntry } : {}), + ...(hasBuilder ? { hasBuilder } : {}), + ...(hasBuilderParam ? { hasBuilderParam } : {}), + }; +} + +export function collectMemoAnnotationImport(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectImport(memoName); +} + +export function collectMemoAnnotationSource(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectSource(memoName, MEMO_IMPORT_SOURCE_NAME); +} + +export function collectMemoableInfoInUnionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSUnionType(node)) { + return currInfo; + } + node.types.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReference(t), + ...collectMemoableInfoInFunctionType(t), + ...collectMemoableInfoInUnionType(t), + }; + }); + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +function collectMemoableInfoInTypeReferencePart(node: arkts.ETSTypeReferencePart): MemoableInfo { + let currInfo: MemoableInfo = {}; + if (!node.typeParams) { + return currInfo; + } + node.typeParams.params.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInUnionType(t), + ...collectMemoableInfoInFunctionType(t), + }; + if (arkts.isETSTypeReference(t) && !!t.part && !!arkts.isETSTypeReferencePart(t.part)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReferencePart(t.part), + }; + } + }); + if (checkIsMemoFromMemoableInfo(currInfo)) { + return { isWithinTypeParams: true }; + } + return { isWithinTypeParams: currInfo.isWithinTypeParams }; +} + +export function collectMemoableInfoInTypeReference(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const metadata = arkts.NodeCache.getInstance().get(node)?.metadata; + return { ...currInfo, ...metadata }; + } + if (!arkts.isETSTypeReference(node) || !node.part || !arkts.isETSTypeReferencePart(node.part)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReferencePart(node.part), + }; + const expr = node.part.name; + let decl: arkts.AstNode | undefined; + if (!expr || !(decl = arkts.getDecl(expr))) { + return currInfo; + } + return { + ...currInfo, + ...collectMemoableInfoInTypeAlias(decl), + }; +} + +export function collectMemoableInfoInFunctionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSFunctionType(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInTypeAlias(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isTSTypeAliasDeclaration(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.typeAnnotation) { + return { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + return currInfo; +} + +export function collectMemoableInfoInParameter(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const metadata = arkts.NodeCache.getInstance().get(node)?.metadata; + return { ...currInfo, ...metadata }; + } + if (!arkts.isEtsParameterExpression(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.type) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.type), + }; + } + if (!!node.initializer) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + if (!!currInfo.isWithinTypeParams) { + const forbidTypeRewrite = !checkIsMemoFromMemoableInfo(currInfo); + arkts.NodeCache.getInstance().collect(node, { forbidTypeRewrite, isWithinTypeParams: true }); + } + return currInfo; +} + +export function collectMemoableInfoInVariableDeclarator(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isVariableDeclarator(node)) { + return currInfo; + } + if (!!node.name.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.name.typeAnnotation), + }; + } + if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + if (!!node.parent && arkts.isVariableDeclaration(node.parent)) { + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node.parent), + }; + } + const decl = arkts.getDecl(node.name); + if (!decl) { + return currInfo; + } + if (arkts.isMethodDefinition(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(decl.scriptFunction), + }; + } else if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + return currInfo; +} + +export function collectMemoableInfoInProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const property = node as arkts.Property; + const hasProperType = !!property.value && arkts.isArrowFunctionExpression(property.value); + return { ...currInfo, hasMemo: true, hasProperType }; + } + if (!arkts.isProperty(node) || !node.key || !arkts.isIdentifier(node.key)) { + return currInfo; + } + const decl = arkts.getDecl(node.key); + if (!decl) { + return currInfo; + } + if (arkts.isMethodDefinition(decl)) { + const newInfo = collectMemoableInfoInMethod(decl); + currInfo = { ...currInfo, ...newInfo }; + } else if (arkts.isClassProperty(decl)) { + const newInfo = collectMemoableInfoInClassProperty(decl); + currInfo = { ...currInfo, ...newInfo }; + } + currInfo.hasProperType = false; + if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { + currInfo.hasProperType = true; + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.value.scriptFunction), + }; + } + return currInfo; +} + +export function collectMemoableInfoInClassProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isClassProperty(node)) { + return currInfo; + } + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + if (!!node.value) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.value), + }; + } + return currInfo; +} + +export function collectMemoableInfoInArrowFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isArrowFunctionExpression(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.scriptFunction) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.scriptFunction), + }; + } + if (!!node.parent && arkts.isAssignmentExpression(node.parent) && !!node.parent.left) { + const expr = arkts.isMemberExpression(node.parent.left) ? node.parent.left.property : node.parent.left; + const decl = arkts.getDecl(expr); + if (!decl) { + return currInfo; + } + if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + } + return currInfo; +} + +export function collectMemoableInfoInScriptFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isScriptFunction(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInMethod(node: arkts.MethodDefinition): MemoableInfo { + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + let info: MemoableInfo = {}; + if (isSetter && node.scriptFunction.params.length > 0) { + if (hasReceiver && node.scriptFunction.params.length === 2) { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(1)!); + } else { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(0)!); + } + } else if (isGetter) { + info = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + } + return collectMemoableInfoInScriptFunction(node.scriptFunction, info); +} + +export function collectMemoableInfoInType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + return { + ...currInfo, + ...collectMemoableInfoInFunctionType(node), + ...collectMemoableInfoInUnionType(node), + ...collectMemoableInfoInTypeReference(node), + }; +} + +export function collectMemoableInfoInFunctionReturnType(node: arkts.ScriptFunction): MemoableInfo { + if (!!node.returnTypeAnnotation) { + let memoableInfo: MemoableInfo; + if (arkts.NodeCache.getInstance().has(node.returnTypeAnnotation)) { + memoableInfo = { hasMemo: true, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInType(node.returnTypeAnnotation); + } + if ((memoableInfo.hasMemo || memoableInfo.hasBuilder) && memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node.returnTypeAnnotation); + } + return memoableInfo; + } + return {}; +} + +export function collectScriptFunctionReturnTypeFromInfo(node: arkts.ScriptFunction, info: MemoableInfo): void { + const returnType = node.returnTypeAnnotation; + if (!returnType || arkts.NodeCache.getInstance().has(returnType)) { + return; + } + const isMemoReturnType = checkIsMemoFromMemoableInfo(info); + const isWithinTypeParams = info.isWithinTypeParams; + if (isMemoReturnType || isWithinTypeParams) { + const forbidTypeRewrite = !isMemoReturnType; + arkts.NodeCache.getInstance().collect(returnType, { forbidTypeRewrite, isWithinTypeParams }); + } +} + +export function collectGensymDeclarator(declarator: arkts.VariableDeclarator, info: MemoableInfo): void { + if (!info.hasMemo && !info.hasBuilder) { + return; + } + arkts.NodeCache.getInstance().collect(declarator); + const initializer = declarator.initializer; + if (!initializer || !arkts.isConditionalExpression(initializer)) { + return; + } + const alternate = initializer.alternate; + if (!alternate) { + return; + } + let arrowFunc: arkts.ArrowFunctionExpression | undefined; + if (arkts.isTSAsExpression(alternate) && !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr)) { + arrowFunc = alternate.expr; + } else if (arkts.isArrowFunctionExpression(alternate)) { + arrowFunc = alternate; + } + if (!!arrowFunc) { + const func = arrowFunc.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(func); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody(body, returnMemoableInfo, paramMemoableInfoMap, gensymCount); + } + } +} + +export function collectMemoableInfoMapInFunctionParams( + node: arkts.ScriptFunction, + shouldCollectParameter: boolean = true +): [Map, number] { + const hasReceiver = node.hasReceiver; + const paramMap: Map = new Map(); + let gensymCount: number = 0; + node.params.slice(hasReceiver ? 1 : 0).forEach((p) => { + const info = collectMemoableInfoInFunctionParam(node, p, gensymCount, shouldCollectParameter); + gensymCount = info.gensymCount; + info.peers.forEach((peer) => paramMap.set(peer, info.memoableInfo)); + }); + return [paramMap, gensymCount]; +} + +interface FunctionParamCollectInfo { + peers: arkts.AstNode['peer'][]; + gensymCount: number; + memoableInfo: MemoableInfo; +} + +function collectMemoableInfoInFunctionParam( + node: arkts.ScriptFunction, + param: arkts.Expression, + gensymCount: number, + shouldCollectParameter: boolean = true +): FunctionParamCollectInfo { + const peers: arkts.AstNode['peer'][] = []; + let memoableInfo: MemoableInfo; + const _param = param as arkts.ETSParameterExpression; + if (arkts.NodeCache.getInstance().has(_param)) { + const metadata = arkts.NodeCache.getInstance().get(_param)!.metadata ?? {}; + const { hasMemoSkip } = metadata; + memoableInfo = { hasMemo: true, hasMemoSkip, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInParameter(_param); + } + if (_param.identifier.name.startsWith(GenSymPrefix.INTRINSIC) && !!node.body && arkts.isBlockStatement(node.body)) { + const declaration = node.body.statements.at(gensymCount); + if (!!declaration && arkts.isVariableDeclaration(declaration) && declaration.declarators.length > 0) { + const declarator = declaration.declarators[0]; + collectGensymDeclarator(declarator, memoableInfo); + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(declarator.name.peer); + } + gensymCount++; + } + } + if (checkIsMemoFromMemoableInfo(memoableInfo)) { + arkts.NodeCache.getInstance().collect(_param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(_param.identifier.peer); + } + return { peers, memoableInfo, gensymCount }; +} + +/** + * Collect `@memo` annotated `arkts.TypeNode` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TypeNode` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): typeAnnotation is arkts.ETSFunctionType { + if (!typeAnnotation) { + return false; + } + const memoableInfo = collectMemoableInfoInType(typeAnnotation); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(typeAnnotation); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.Property` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.Property` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromProperty(property: arkts.AstNode): property is arkts.Property { + const memoableInfo = collectMemoableInfoInProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + const hasBuilder = !!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam; + return hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ClassProperty` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ClassProperty` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromClassProperty(property: arkts.AstNode): property is arkts.ClassProperty { + const memoableInfo = collectMemoableInfoInClassProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + const hasBuilderType = !!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam; + if (!!memoableInfo.isWithinTypeParams) { + arkts.NodeCache.getInstance().collect(property, { isWithinTypeParams: true }); + } + return hasBuilderType && !memoableInfo.hasMemo && !!memoableInfo.hasProperType && !memoableInfo.isWithinTypeParams; +} + +/** + * Collect `@memo` annotated `arkts.ETSParameterExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ETSParameterExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromParameter(param: arkts.AstNode | undefined): param is arkts.ETSParameterExpression { + if (!param) { + return false; + } + const memoableInfo = collectMemoableInfoInParameter(param); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ArrowFunctionExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ArrowFunctionExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { + if (!arkts.isArrowFunctionExpression(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInArrowFunction(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const func = node.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + arkts.NodeCache.getInstance().collect(node, { hasMemoEntry, hasMemoIntrinsic }); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.TSTypeAliasDeclaration` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TSTypeAliasDeclaration` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAlias(node: arkts.AstNode): node is arkts.TSTypeAliasDeclaration { + const memoableInfo = collectMemoableInfoInTypeAlias(node); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.MethodDefinition` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.MethodDefinition` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromMethod(node: arkts.AstNode): node is arkts.MethodDefinition { + if (!arkts.isMethodDefinition(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInMethod(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const func = node.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + func, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + const metadata = collectMetadataInMethod(node); + arkts.NodeCache.getInstance().collect(node, { + ...metadata, + hasMemoEntry, + hasMemoIntrinsic, + }); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.TSTypeParameterInstantiation` node from corresponding call's typeParams. + * + * @param node `arkts.TSTypeParameterInstantiation` node. + */ +export function collectMemoFromTSTypeParameterInstantiation(node: arkts.TSTypeParameterInstantiation): void { + node.params.forEach((t) => { + const typeInfo = collectMemoableInfoInType(t); + if (checkIsMemoFromMemoableInfo(typeInfo)) { + arkts.NodeCache.getInstance().collect(t); + } + }); +} + +/** + * Collect `@memo` annotated `arkts.ETSNewClassInstanceExpression` node from corresponding new class type reference. + * + * @param node `arkts.ETSNewClassInstanceExpression` node. + */ +export function collectMemoFromNewClass(node: arkts.ETSNewClassInstanceExpression): void { + const typeRef = node.getTypeRef; + if (!typeRef || !arkts.isETSTypeReference(typeRef)) { + return; + } + const typeInfo = collectMemoableInfoInTypeReference(typeRef); + if (typeInfo.isWithinTypeParams) { + arkts.NodeCache.getInstance().collect(typeRef, { isWithinTypeParams: true }); + } +} + +/** + * Collect `@memo` annotated `arkts.CallExpression` node from corresponding declared method, + * as well as collect each `@memo` annotated argument from corresponding declared method parameter. + * + * @param node `arkts.CallExpression` node. + */ +export function collectMemoFromCallExpression(node: arkts.CallExpression): void { + if (arkts.NodeCache.getInstance().has(node)) { + return; + } + const typeParams = node.typeParams; + if (!!typeParams) { + collectMemoFromTSTypeParameterInstantiation(typeParams); + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + return; + } + let isCollected: boolean = false; + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + isCollected = true; + } + if (arkts.isMethodDefinition(decl)) { + isCollected = collectCallWithDeclaredMethod(node, decl); + } else if (arkts.isClassProperty(decl)) { + isCollected = collectCallWithDeclaredClassProperty(node, decl); + } + if (isCollected && arkts.isTSAsExpression(node.expression) && node.expression.typeAnnotation) { + arkts.NodeCache.getInstance().collect(node.expression.typeAnnotation); + } +} + +export function collectCallWithDeclaredClassProperty(node: arkts.CallExpression, decl: arkts.ClassProperty): boolean { + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + return true; + } + const memoableInfo = collectMemoableInfoInClassProperty(decl); + if (checkIsMemoFromMemoableInfo(memoableInfo, false) || memoableInfo.hasBuilder || memoableInfo.hasBuilderParam) { + arkts.NodeCache.getInstance().collect(node); + return true; + } + return false; +} + +export function collectCallWithDeclaredMethod(node: arkts.CallExpression, decl: arkts.MethodDefinition): boolean { + const hasReceiver = decl.scriptFunction.hasReceiver; + const params = decl.scriptFunction.params; + const args = node.arguments; + const hasRestParameter = decl.scriptFunction.hasRestParameter; + const isTrailingCall = node.isTrailingCall; + const options = { hasRestParameter, isTrailingCall }; + forEachArgWithParam(args, params, collectCallArgsWithMethodParams, options); + if (arkts.NodeCache.getInstance().has(decl)) { + const { hasMemoEntry, hasMemoIntrinsic } = arkts.NodeCache.getInstance().get(decl)!.metadata ?? {}; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + return true; + } else { + const memoableInfo = collectMemoableInfoInScriptFunction(decl.scriptFunction); + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + return true; + } + } + return false; +} + +export function collectCallArgsWithMethodParams(arg: arkts.Expression | undefined, param: arkts.Expression): void { + if (!arg) { + return; + } + let info: MemoableInfo; + if (arkts.NodeCache.getInstance().has(param)) { + info = { hasMemo: true, hasProperType: true }; + } else { + info = collectMemoableInfoInParameter(param); + } + if (checkIsMemoFromMemoableInfo(info) && arkts.isArrowFunctionExpression(arg)) { + arkts.NodeCache.getInstance().collect(arg); + const func = arg.scriptFunction; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(func); + collectScriptFunctionReturnTypeFromInfo(func, returnMemoableInfo); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(func); + const body = func.body; + if (!!body && arkts.isBlockStatement(body)) { + collectMemoScriptFunctionBody(body, returnMemoableInfo, paramMemoableInfoMap, gensymCount); + } + } +} + +export function findIdentifierFromCallee(callee: arkts.AstNode | undefined): arkts.Identifier | undefined { + if (!callee) { + return undefined; + } + if (arkts.isIdentifier(callee)) { + return callee; + } + if (arkts.isMemberExpression(callee)) { + return findIdentifierFromCallee(callee.property); + } + if (arkts.isTSAsExpression(callee)) { + return findIdentifierFromCallee(callee.expr); + } + if (arkts.isTSNonNullExpression(callee)) { + return findIdentifierFromCallee(callee.expr); + } + return undefined; +} + +export function collectMemoScriptFunctionBody( + body: arkts.BlockStatement, + returnMemoableInfo: MemoableInfo, + paramMemoableInfoMap: Map, + gensymCount: number, + disableCollectReturn?: boolean +): void { + const collector = new MemoFunctionCollector(); + body.statements.forEach((st, index) => { + if (index < gensymCount) { + return; + } + if (disableCollectReturn) { + collector.disableCollectReturn(); + } + collector.registerReturnInfo(returnMemoableInfo).registerParamInfoMap(paramMemoableInfoMap).visitor(st); + collector.reset(); + }); +} + +export function collectMetadataInMethod(node: arkts.MethodDefinition): arkts.AstNodeCacheValue['metadata'] { + const callName = node.name.name; + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + return { callName, hasReceiver, isSetter, isGetter }; +} + +export function checkIsMemoFromMemoableInfo(info: MemoableInfo, ignoreType: boolean = false): boolean { + return ( + (!!info.hasMemo || !!info.hasMemoIntrinsic || !!info.hasMemoEntry || !!info.hasBuilder) && + (ignoreType || !!info.hasProperType) + ); +} + +export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { + const decl = arkts.getDecl(node); + if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { + if (!!decl.parent.initializer && arkts.isIdentifier(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer); + } + if (!!decl.parent.initializer && arkts.isMemberExpression(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer.property); + } + } + return decl; +} + +export function parametersBlockHasReceiver(params: readonly arkts.Expression[]): boolean { + return params.length > 0 && arkts.isEtsParameterExpression(params[0]) && isThisParam(params[0]); +} + +export function parametrizedNodeHasReceiver(node: arkts.ScriptFunction | arkts.ETSFunctionType | undefined): boolean { + if (node === undefined) { + return false; + } + return parametersBlockHasReceiver(node.params); +} + +function isThisParam(node: arkts.Expression | undefined): boolean { + if (node === undefined || !arkts.isEtsParameterExpression(node)) { + return false; + } + return node.identifier?.isReceiver ?? false; +} diff --git a/arkui-plugins/common/arkts-utils.ts b/arkui-plugins/common/arkts-utils.ts index e62d9c99169db550168ef7368d2233af08ccdc9e..ff2e195a86da1239072344f8e94926b48c75c1dd 100644 --- a/arkui-plugins/common/arkts-utils.ts +++ b/arkui-plugins/common/arkts-utils.ts @@ -14,6 +14,37 @@ */ import * as arkts from '@koalaui/libarkts'; +import { DeclarationCollector } from './declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, DecoratorNames } from './predefines'; + +export function coerceToAstNode(node: arkts.AstNode): T { + return node as T; +} + +/** + * create and insert `import { as } from ` to the top of script's statements. + */ +export function createAndInsertImportDeclaration( + source: arkts.StringLiteral, + imported: arkts.Identifier, + local: arkts.Identifier, + importKind: arkts.Es2pandaImportKinds, + program: arkts.Program +): void { + const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( + source, + [arkts.factory.createImportSpecifier(imported, local)], + importKind, + program, + arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE + ); + arkts.importDeclarationInsert(importDecl, program); + return; +} + +export function isNumeric(str: string): boolean { + return /^\d+$/.test(str); +} export function annotation(name: string): arkts.AnnotationUsage { const ident: arkts.Identifier = arkts.factory.createIdentifier(name).setAnnotationUsage(); @@ -29,6 +60,28 @@ export function isAnnotation(node: arkts.AnnotationUsage, annoName: string) { return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === annoName; } +export function isDecoratorAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorNames, + ignoreDecl?: boolean +): boolean { + if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { + return false; + } + if (!ignoreDecl) { + const decl = arkts.getDecl(anno.expr); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; +} + export function removeAnnotationByName( annotations: readonly arkts.AnnotationUsage[], annoName: string @@ -80,24 +133,6 @@ export function matchPrefix(prefixCollection: (string | RegExp)[], name: string) return false; } -export function updateStructMetadata( - structInfo: arkts.StructInfo, - propertyName: string, - properties: string[], - modifiers: arkts.Es2pandaModifierFlags, - hasStateManagementType?: boolean -): arkts.StructInfo { - const metadata: Record = structInfo.metadata ?? {}; - metadata[propertyName] = { - name: propertyName, - properties, - modifiers, - hasStateManagementType, - }; - structInfo.metadata = metadata; - return structInfo; -} - export function moveToFront(arr: T[], idx: number): T[] { if (idx < 0 || idx >= arr.length) { throw new Error(`Index ${idx} is out of bounds for array of length ${arr.length}`); @@ -106,4 +141,40 @@ export function moveToFront(arr: T[], idx: number): T[] { const copy = [...arr]; const [item] = copy.splice(idx, 1); return [item, ...copy]; -} \ No newline at end of file +} + +/** + * Performs the specified action for each argument in a `arkts.CallExpression`'s arguments array + * paired with corresponding parameter from the function declaration node. + * + * @param args An arguments array from a `arkts.CallExpression` node. + * @param params A parameters array from a function declaration node. + * @param callbackFn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param options Additional options field that accepts special conditions of calls and function, used for pairing arguments with parameters. + */ +export function forEachArgWithParam( + args: readonly arkts.Expression[], + params: readonly arkts.Expression[], + callbackFn: (arg: arkts.Expression | undefined, param: arkts.Expression, index?: number) => void, + options?: { isTrailingCall?: boolean; hasReceiver?: boolean; hasRestParameter?: boolean } +): void { + const argLen: number = args.length; + const paramLen: number = params.length; + if (argLen === 0 || paramLen === 0) { + return; + } + const hasRestParam: boolean = !!options?.hasRestParameter; + const isTrailingCall: boolean = !!options?.isTrailingCall; + const maxLen = hasRestParam ? argLen : paramLen; + let index: number = 0; + while (index < maxLen - 1) { + const param = params.at(index) ?? params.at(paramLen - 1)!; + const argument = isTrailingCall && index >= argLen - 1 ? undefined : args.at(index); + callbackFn(argument, param, index); + index++; + } + const lastParam = params.at(paramLen - 1)!; + const lastIndex = isTrailingCall ? argLen - 1 : maxLen - 1; + const lastArg = args.at(lastIndex); + callbackFn(lastArg, lastParam, maxLen - 1); +} diff --git a/arkui-plugins/common/debug.ts b/arkui-plugins/common/debug.ts index f39940e8f7508e4286c59d687e42c1a4873a9b10..5be9e280c18f04c0abb94051e2990bea76169610 100644 --- a/arkui-plugins/common/debug.ts +++ b/arkui-plugins/common/debug.ts @@ -19,8 +19,9 @@ import * as arkts from '@koalaui/libarkts'; const isDebugLog: boolean = false; const isDebugDump: boolean = false; const isPerformance: boolean = false; +const enableMemoryTracker: boolean = false; arkts.Performance.getInstance().skip(!isPerformance); - +arkts.Performance.getInstance().enableMemoryTracker(enableMemoryTracker); export function getEnumName(enumType: any, value: number): string | undefined { return enumType[value]; } @@ -46,7 +47,7 @@ export function debugDump( const outputDir: string = cachePath ? path.resolve(currentDirectory, cachePath, modifiedFileName) : path.resolve(currentDirectory, 'dist', 'cache', modifiedFileName); - const filePath: string = path.resolve(outputDir, fileName); + const filePath: string = path.resolve(outputDir, fileName.replaceAll('\/', '_')); if (!fs.existsSync(outputDir)) { mkDir(outputDir); } diff --git a/arkui-plugins/common/declaration-collector.ts b/arkui-plugins/common/declaration-collector.ts new file mode 100644 index 0000000000000000000000000000000000000000..d65f0d40da094bc237037b592d7b699e322c77bd --- /dev/null +++ b/arkui-plugins/common/declaration-collector.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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 { IMPORT_SOURCE_MAP_V2, INTERMEDIATE_IMPORT_SOURCE } from './predefines'; +import { ImportCollector } from './import-collector'; + +export class DeclarationCollector { + private fromExternalSourceNameMap: Map; + private fromExternalSourceNodePeerMap: Map; + static instance: DeclarationCollector; + + private constructor() { + this.fromExternalSourceNameMap = new Map(); + this.fromExternalSourceNodePeerMap = new Map(); + } + + static getInstance(): DeclarationCollector { + if (!this.instance) { + this.instance = new DeclarationCollector(); + } + return this.instance; + } + + private collectIntermediateImportSource(symbol: string, declSourceName: string): void { + let sourceName: string; + if (IMPORT_SOURCE_MAP_V2.has(symbol)) { + sourceName = IMPORT_SOURCE_MAP_V2.get(symbol)!; + } else { + sourceName = declSourceName; + } + ImportCollector.getInstance().collectSource(symbol, sourceName); + } + + collect(decl: arkts.AstNode | undefined): void { + if (!decl) { + return; + } + let declName: string | undefined; + if (arkts.isAnnotationDeclaration(decl) && !!decl.expr && arkts.isIdentifier(decl.expr)) { + declName = decl.expr.name; + } else if (arkts.isMethodDefinition(decl)) { + declName = decl.name.name; + } else if (arkts.isIdentifier(decl)) { + declName = decl.name; + } else if (arkts.isClassProperty(decl) && !!decl.key && arkts.isIdentifier(decl.key)) { + declName = decl.key.name; + } else if (arkts.isEtsParameterExpression(decl)) { + declName = decl.identifier.name; + } + if (!declName) { + return; + } + let sourceName: string = arkts.getProgramFromAstNode(decl).moduleName; + this.fromExternalSourceNameMap.set(declName, sourceName); + this.fromExternalSourceNodePeerMap.set(decl.peer, sourceName); + + INTERMEDIATE_IMPORT_SOURCE.get(declName)?.forEach((symbol) => { + this.collectIntermediateImportSource(symbol, sourceName); + }); + } + + findExternalSourceFromName(declName: string): string | undefined { + return this.fromExternalSourceNameMap.get(declName); + } + + findExternalSourceFromNode(decl: arkts.AstNode): string | undefined { + return this.fromExternalSourceNodePeerMap.get(decl.peer); + } + + reset(): void { + this.fromExternalSourceNameMap.clear(); + this.fromExternalSourceNodePeerMap.clear(); + } +} diff --git a/arkui-plugins/common/import-collector.ts b/arkui-plugins/common/import-collector.ts new file mode 100644 index 0000000000000000000000000000000000000000..935b339af29f87951a69cc85508b64f1fa83e810 --- /dev/null +++ b/arkui-plugins/common/import-collector.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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 { createAndInsertImportDeclaration } from './arkts-utils'; + +interface ImportInfo { + imported: string; + local: string; + source: string; + kind: arkts.Es2pandaImportKinds; +} + +function insertImport(importInfo: ImportInfo, program?: arkts.Program): void { + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(importInfo.source); + const imported: arkts.Identifier = arkts.factory.createIdentifier(importInfo.imported); + const local: arkts.Identifier = arkts.factory.createIdentifier(importInfo.local); + // Insert this import at the top of the script's statements. + if (!program) { + throw Error('Failed to insert import: Transformer has no program'); + } + createAndInsertImportDeclaration(source, imported, local, importInfo.kind, program); +} + +export class ImportCollector { + public importInfos: ImportInfo[]; + public localMap: Map; + public sourceMap: Map; + private static instance: ImportCollector; + + /** this set is used for keeping the import sentence unique */ + private imported: Set; + + private constructor() { + this.importInfos = []; + this.imported = new Set(); + this.localMap = new Map(); + this.sourceMap = new Map(); + } + + static getInstance(): ImportCollector { + if (!this.instance) { + this.instance = new ImportCollector(); + } + return this.instance; + } + + reset(): void { + this.importInfos = []; + this.imported.clear(); + this.localMap.clear(); + this.sourceMap.clear(); + } + + collectSource(imported: string, source: string): void { + if (!this.sourceMap.has(imported)) { + this.sourceMap.set(imported, source); + } + } + + collectImport( + imported: string, + local?: string, + kind: arkts.Es2pandaImportKinds = arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE + ): void { + if (!this.sourceMap.has(imported)) { + throw new Error(`ImportCollector: import ${imported}'s source haven't been collected yet.`); + } + if (this.imported.has(imported)) { + return; + } + const source: string = this.sourceMap.get(imported)!; + const _local: string = local ?? imported; + this.importInfos.push({ + source, + imported, + local: _local, + kind, + }); + this.localMap.set(imported, _local); + this.imported.add(imported); + } + + getLocal(imported: string): string | undefined { + return this.localMap.get(imported); + } + + insertCurrentImports(program?: arkts.Program): void { + this.importInfos.forEach((importInfo) => { + insertImport(importInfo, program); + }); + } +} diff --git a/arkui-plugins/common/log-collector.ts b/arkui-plugins/common/log-collector.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fa9ed7af815dca0e10d9c5bc469f3477762227d --- /dev/null +++ b/arkui-plugins/common/log-collector.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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 { LogType } from './predefines'; + +interface LogInfo { + type: LogType; + message: string; + node: arkts.AstNode; + code: string; +} + +export function generateDiagnosticKind(logItem: LogInfo): arkts.DiagnosticKind { + return arkts.DiagnosticKind.create( + `${logItem.code}: ${logItem.message}`, + logItem.type === LogType.ERROR + ? arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR + : arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_WARNING + ); +} + +export class LogCollector { + public logInfos: LogInfo[]; + private static instance: LogCollector; + private ignoreError: boolean; + + private constructor() { + this.logInfos = []; + this.ignoreError = false; + } + + static getInstance(): LogCollector { + if (!this.instance) { + this.instance = new LogCollector(); + } + return this.instance; + } + + reset(): void { + this.logInfos = []; + this.ignoreError = false; + } + + collectLogInfo(logItem: LogInfo): void { + this.logInfos.push(logItem); + } + + emitLogInfo(): void { + if (this.ignoreError) { + return; + } + this.logInfos.forEach((logItem: LogInfo) => { + arkts.Diagnostic.logDiagnostic(generateDiagnosticKind(logItem), arkts.getStartPosition(logItem.node)); + }); + } + + shouldIgnoreError(ignoreError: boolean | undefined): void { + if (!!ignoreError) { + this.ignoreError = true; + } + } +} diff --git a/arkui-plugins/common/plugin-context.ts b/arkui-plugins/common/plugin-context.ts index 95cce91523cab960aa06117adfc26166097a9857..f661350a0fbf5d86de9efddb843f24c66dac55a1 100644 --- a/arkui-plugins/common/plugin-context.ts +++ b/arkui-plugins/common/plugin-context.ts @@ -21,12 +21,14 @@ export class PluginContext { private program: arkts.Program | undefined; private projectConfig: ProjectConfig | undefined; private contextPtr: number | undefined; + private codingFilePath: string | undefined; constructor() { this.ast = undefined; this.program = undefined; this.projectConfig = undefined; this.contextPtr = undefined; + this.codingFilePath = undefined; } /** @@ -58,7 +60,7 @@ export class PluginContext { } public setProjectConfig(projectConfig: ProjectConfig): void { - throw new Error('do not set projectConfig!'); + this.projectConfig = projectConfig; } public getProjectConfig(): ProjectConfig | undefined { @@ -72,12 +74,53 @@ export class PluginContext { public getContextPtr(): number | undefined { return this.contextPtr; } + + public setCodingFilePath(codingFilePath: string): void { + this.codingFilePath = codingFilePath; + } + + public getCodingFilePath(): string | undefined { + return this.codingFilePath; + } + + public isCoding(): boolean { + return this.codingFilePath !== undefined; + } +} + +export interface DependentModuleConfig { + packageName: string; + moduleName: string; + moduleType: string; + modulePath: string; + sourceRoots: string[]; + entryFile: string; + language: string; + declFilesPath?: string; + dependencies?: string[]; } export interface ProjectConfig { bundleName: string; moduleName: string; cachePath: string; + dependentModuleList: DependentModuleConfig[]; + appResource: string; + rawFileResource: string; + buildLoaderJson: string; + hspResourcesMap: boolean; + compileHar: boolean; + byteCodeHar: boolean; + uiTransformOptimization: boolean; + resetBundleName: boolean; + allowEmptyBundleName: boolean; + moduleType: string; + moduleRootPath: string; + aceModuleJsonPath: string; + ignoreError: boolean; + projectPath: string, + projectRootPath: string, + integratedHsp: boolean frameworkMode?: string; } diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 64b90a29b526260b820c4a94cf2b7910485cac7a..b180fdddc1f06615bcb55d71ce08e624577e6e01 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -16,15 +16,9 @@ export const EXTERNAL_SOURCE_PREFIX_NAMES: (string | RegExp)[] = [ 'std', 'escompat', - 'security', - 'application', - 'permissions', - 'bundleManager', - 'commonEvent', /@arkts\..*/, /@ohos\.(?!arkui).*/, /@system\..*/, - /ability\..*/, ]; export const EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK: (string | RegExp)[] = [ @@ -33,62 +27,364 @@ export const EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK: (string | RegExp)[] = [ /@arkts\..*/ ]; -export const ARKUI_COMPONENT_IMPORT_NAME: string = '@ohos.arkui.component'; -export const ARKUI_STATEMANAGEMENT_IMPORT_NAME: string = '@ohos.arkui.stateManagement'; -export const KIT_ARKUI_NAME: string = '@kit.ArkUI'; +export const ARKUI_IMPORT_PREFIX_NAMES: (string | RegExp)[] = [/arkui\..*/, /@ohos\..*/, /@kit\..*/]; -export const EXTERNAL_SOURCE_ALLOWED_IMPORT_INSERT_NAMES: string[] = [ - ARKUI_COMPONENT_IMPORT_NAME, - ARKUI_STATEMANAGEMENT_IMPORT_NAME, -]; +export const MEMO_IMPORT_SOURCE_NAME: string = 'arkui.stateManagement.runtime'; +export const CUSTOM_COMPONENT_IMPORT_SOURCE_NAME: string = 'arkui.component.customComponent'; +export const CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME: string = 'arkui.component.customDialogController'; +export const ENTRY_POINT_IMPORT_SOURCE_NAME: string = 'arkui.UserView'; +export const ARKUI_COMPONENT_COMMON_SOURCE_NAME: string = 'arkui.component.common'; +export const ARKUI_FOREACH_SOURCE_NAME: string = 'arkui.component.forEach'; +export const ARKUI_BUILDER_SOURCE_NAME: string = 'arkui.component.builder'; + +export enum ModuleType { + HAR = 'har', + ENTRY = 'entry', + FEATURE = 'feature', + SHARED = 'shared', +} + +export enum DefaultConfiguration { + HAR_DEFAULT_MODULE_NAME = '__harDefaultModuleName__', + HAR_DEFAULT_BUNDLE_NAME = '__harDefaultBundleName__', + DYNAMIC_MODULE_NAME = '__MODULE_NAME__', + DYNAMIC_BUNDLE_NAME = '__BUNDLE_NAME__', +} + +export enum LogType { + ERROR = 'ERROR', + WARN = 'WARN', +} + +export enum Dollars { + DOLLAR_RESOURCE = '$r', + DOLLAR_RAWFILE = '$rawfile', + DOLLAR_DOLLAR = '$$', + TRANSFORM_DOLLAR_RESOURCE = '_r', + TRANSFORM_DOLLAR_RAWFILE = '_rawfile', +} -export const IMPORT_SOURCE_MAP: Map> = new Map>([ - ['arkui.stateManagement.runtime', new Set(['memo', '__memo_context_type', '__memo_id_type'])] +export enum BindableDecl { + BINDABLE = 'Bindable', +} + +export enum StructDecoratorNames { + ENTRY = 'Entry', + COMPONENT = 'Component', + COMPONENT_V2 = 'ComponentV2', + RESUABLE = 'Reusable', + RESUABLE_V2 = 'ReusableV2', + CUSTOM_LAYOUT = 'CustomLayout', + CUSTOMDIALOG = 'CustomDialog', +} + +export enum EntryWrapperNames { + ENTRY_FUNC = 'entry', + WRAPPER_CLASS_NAME = '__EntryWrapper', + ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME = '_entry_local_storage_', + ENTRY_POINT_CLASS_NAME = 'EntryPoint', + REGISTER_NAMED_ROUTER = 'RegisterNamedRouter', + ROUTER_NAME = 'routerName', + INSTANCE = 'instance', + PARAM = 'param' +} + +export enum ObservedNames { + V1_RERENDER_ID = '____V1RenderId', + RERENDER_ID = 'renderId', + SET_V1_RERENDER_ID = 'setV1RenderId', + META = 'meta', + CONDITIONAL_ADD_REF = 'conditionalAddRef', + ADD_REF = 'addRef', + SHOULD_ADD_REF = 'shouldAddRef', + NEW_NAME = 'newName', + PROPERTY_PREFIX = '', + NEW_VALUE = 'newValue', + FIRE_CHANGE = 'fireChange', + EXECUATE_WATCHES = 'executeOnSubscribingWatches', +} + +export enum MonitorNames { + PATH = 'path', + VALUE_CALL_CACK = 'valueCallback', + I_MONITOR = 'IMonitor', + M_PARAM = '_m', +} + +export enum EntryParamNames { + ENTRY_STORAGE = 'storage', + ENTRY_USE_SHARED_STORAGE = 'useSharedStorage', + ENTRY_ROUTE_NAME = 'routeName' +} + +export enum InnerComponentNames { + FOR_EACH = 'ForEach', +} + +export enum DecoratorNames { + STATE = 'State', + STORAGE_LINK = 'StorageLink', + STORAGE_PROP = 'StorageProp', + LINK = 'Link', + PROP = 'Prop', + PROVIDE = 'Provide', + CONSUME = 'Consume', + OBJECT_LINK = 'ObjectLink', + OBSERVED = 'Observed', + OBSERVED_V2 = 'ObservedV2', + WATCH = 'Watch', + BUILDER_PARAM = 'BuilderParam', + BUILDER = 'Builder', + CUSTOM_DIALOG = 'CustomDialog', + LOCAL_STORAGE_PROP = 'LocalStorageProp', + LOCAL_STORAGE_LINK = 'LocalStorageLink', + REUSABLE = 'Reusable', + TRACK = 'Track', + TRACE = 'Trace', + JSONSTRINGIFYIGNORE = 'JSONStringifyIgnore', + JSONRENAME = 'JSONRename', + ANIMATABLE_EXTEND = 'AnimatableExtend', + PROP_REF = 'PropRef', + LOCAL = 'Local', + LOCAL_STORAGE_PROP_REF = 'LocalStoragePropRef', + STORAGE_PROP_REF = 'StoragePropRef', + PARAM = 'Param', + ONCE = 'Once', + PROVIDER = 'Provider', + CONSUMER = 'Consumer', + MONITOR = 'Monitor', + COMPUTED = 'Computed', + EVENT = 'Event', + REQUIRE = 'Require' +} + +export enum TypeNames { + NULLISH_TYPE = 'NullishType', + ANY = 'Any', + ARRAY = 'Array', +} + +export enum DecoratorIntrinsicNames { + LINK = '__Link_intrinsic', +} + +export enum StateManagementTypes { + STATE_MANAGEMENT_FACTORY = 'STATE_MGMT_FACTORY', + STATE_DECORATED = 'IStateDecoratedVariable', + LINK_DECORATED = 'ILinkDecoratedVariable', + LINK_SOURCE_TYPE = 'LinkSourceType', + STORAGE_LINK_DECORATED = 'IStorageLinkDecoratedVariable', + STORAGE_PROP_REF_DECORATED = 'IStoragePropRefDecoratedVariable', + LOCAL_STORAGE_LINK_DECORATED = 'ILocalStorageLinkDecoratedVariable', + PROP_DECORATED = 'IPropDecoratedVariable', + SYNCED_PROPERTY = 'SyncedProperty', + PROVIDE_DECORATED = 'IProvideDecoratedVariable', + CONSUME_DECORATED = 'IConsumeDecoratedVariable', + OBJECT_LINK_DECORATED = 'IObjectLinkDecoratedVariable', + PROP_REF_DECORATED = 'IPropRefDecoratedVariable', + LOCAL_DECORATED = 'ILocalDecoratedVariable', + LOCAL_STORAGE_PROP_REF_DECORATED = 'ILocalStoragePropRefDecoratedVariable', + PARAM_DECORATED = 'IParamDecoratedVariable', + ONCE_DECORATED = 'IParamOnceDecoratedVariable', + PROVIDER_DECORATED = 'IProviderDecoratedVariable', + CONSUMER_DECORATED = 'IConsumerDecoratedVariable', + COMPUTED_DECORATED = 'IComputedDecoratedVariable', + MONITOR_DECORATED = 'IMonitorDecoratedVariable', + MUTABLE_STATE_META = 'IMutableStateMeta', + OBSERVED_OBJECT = 'IObservedObject', + WATCH_ID_TYPE = 'WatchIdType', + RENDER_ID_TYPE = 'RenderIdType', + OBSERVE = 'OBSERVE', + META = '__meta', + SUBSCRIBED_WATCHES = 'ISubscribedWatches', + STORAGE_LINK_STATE = 'StorageLinkState', + OBSERVABLE_PROXY = 'observableProxy', + PROP_STATE = 'propState', + UPDATE = 'update', + MAKE_STATE = 'makeState', + MAKE_LINK = 'makeLink', + MAKE_PROP = 'makeProp', + MAKE_PROP_REF = 'makePropRef', + MAKE_LOCAL = 'makeLocal', + MAKE_STATIC_LOCAL = 'makeStaticLocal', + MAKE_STORAGE_PROP_REF = 'makeStoragePropRef', + MAKE_LOCAL_STORAGE_PROP_REF = 'makeLocalStoragePropRef', + MAKE_STORAGE_LINK = 'makeStorageLink', + MAKE_LOCAL_STORAGE_LINK = 'makeLocalStorageLink', + MAKE_PROVIDE = 'makeProvide', + MAKE_CONSUME = 'makeConsume', + MAKE_OBJECT_LINK = 'makeObjectLink', + MAKE_SUBSCRIBED_WATCHES = 'makeSubscribedWatches', + MAKE_MUTABLESTATE_META = 'makeMutableStateMeta', + MAKE_PARAM = 'makeParam', + MAKE_PARAM_ONCE = 'makeParamOnce', + MAKE_PROVIDER = 'makeProvider', + MAKE_CONSUMER = 'makeConsumer', + MAKE_COMPUTED = 'makeComputed', + MAKE_MONITOR = 'makeMonitor', + UI_UTILS = 'UIUtils', + MAKE_OBSERVED = 'makeObserved', +} + +export enum AnimationNames { + ANIMATABLE_ARITHMETIC = 'AnimatableArithmetic', + CREATE_OR_SET_ANIMATABLEPROPERTY = '__createOrSetAnimatableProperty', + ANIMATION = 'animation', + ANIMATION_START = 'animationStart', + ANIMATION_STOP = 'animationStop', +} + +export enum NavigationNames { + NAVINTERFACE = 'NavInterface', + BUNDLE_NAME = 'bundleName', + MODULE_NAME = 'moduleName', + PAGE_PATH = 'pagePath', + PAGE_FULL_PATH = 'pageFullPath', + INTEGRATED_HSP = 'integratedHsp', +} + +export enum ConditionNames { + CONDITION_SCOPE = 'ConditionScope', + CONDITION_BRANCH = 'ConditionBranch', +} + +export enum ArkTsDefaultNames { + DEFAULT_STATIC_BLOCK_NAME = '', +} + +export const RESOURCE_TYPE: Record = { + color: 10001, + float: 10002, + string: 10003, + plural: 10004, + boolean: 10005, + intarray: 10006, + integer: 10007, + pattern: 10008, + strarray: 10009, + media: 20000, + rawfile: 30000, + symbol: 40000, +}; + +export const DECORATOR_TYPE_MAP = new Map([ + [DecoratorNames.STATE, StateManagementTypes.STATE_DECORATED], + [DecoratorNames.LINK, StateManagementTypes.LINK_SOURCE_TYPE], + [DecoratorNames.PROP, StateManagementTypes.PROP_DECORATED], + [DecoratorNames.PROP_REF, StateManagementTypes.PROP_REF_DECORATED], + [DecoratorNames.STORAGE_LINK, StateManagementTypes.STORAGE_LINK_DECORATED], + [DecoratorNames.STORAGE_PROP, StateManagementTypes.STORAGE_PROP_REF_DECORATED], + [DecoratorNames.STORAGE_PROP_REF, StateManagementTypes.STORAGE_PROP_REF_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP_REF, StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP, StateManagementTypes.SYNCED_PROPERTY], + [DecoratorNames.LOCAL_STORAGE_LINK, StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED], + [DecoratorNames.OBJECT_LINK, StateManagementTypes.OBJECT_LINK_DECORATED], + [DecoratorNames.PROVIDE, StateManagementTypes.PROVIDE_DECORATED], + [DecoratorNames.CONSUME, StateManagementTypes.CONSUME_DECORATED], + [DecoratorNames.LOCAL, StateManagementTypes.LOCAL_DECORATED], + [DecoratorNames.PARAM, StateManagementTypes.PARAM_DECORATED], + [DecoratorNames.ONCE, StateManagementTypes.ONCE_DECORATED], + [DecoratorNames.PROVIDER, StateManagementTypes.PROVIDER_DECORATED], + [DecoratorNames.CONSUMER, StateManagementTypes.CONSUMER_DECORATED], ]); -export const OUTPUT_DEPENDENCY_MAP: Map = new Map([ - ['$r', ['_r']], - ['$rawfile', ['_rawfile']], - ['State', ['StateDecoratedVariable']], - ['Link', ['LinkDecoratedVariable', 'DecoratedV1VariableBase']], - ['Prop', ['PropDecoratedVariable']], - ['Provide', ['ProvideDecoratedVariable']], - ['Consume', ['ConsumeDecoratedVariable']], - ['StorageProp', ['StoragePropDecoratedVariable']], - ['StorageLink', ['StorageLinkDecoratedVariable']], - ['LocalStorageLink', ['StorageLinkState', 'MutableState', 'observableProxy']], - ['LocalStorageProp', ['StorageLinkState', 'SyncedProperty', 'observableProxy', 'propState']], - ['ObjectLink', ['ObjectLinkDecoratedVariable']], - ['Observed', ['MutableStateMeta', 'BackingValue', 'setObservationDepth', 'IObservedObject', 'int32', 'WatchIdType', 'SubscribedWatches']], - ['Track', ['MutableStateMeta', 'BackingValue', 'setObservationDepth', 'IObservedObject', 'int32', 'WatchIdType', 'SubscribedWatches']], - ['$$', ['Bindable']], +export const INTERMEDIATE_IMPORT_SOURCE: Map = new Map([ + [Dollars.DOLLAR_RESOURCE, [Dollars.TRANSFORM_DOLLAR_RESOURCE]], + [Dollars.DOLLAR_RAWFILE, [Dollars.TRANSFORM_DOLLAR_RAWFILE]], + [Dollars.DOLLAR_DOLLAR, [BindableDecl.BINDABLE]], + [DecoratorNames.STATE, [StateManagementTypes.STATE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LINK, [StateManagementTypes.LINK_DECORATED, StateManagementTypes.LINK_SOURCE_TYPE, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROP, [StateManagementTypes.PROP_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROP_REF, [StateManagementTypes.PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROVIDE, [StateManagementTypes.PROVIDE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.CONSUME, [StateManagementTypes.CONSUME_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_PROP, [StateManagementTypes.STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_PROP_REF, [StateManagementTypes.STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL_STORAGE_PROP_REF, [StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.STORAGE_LINK, [StateManagementTypes.STORAGE_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.OBJECT_LINK, [StateManagementTypes.OBJECT_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL_STORAGE_LINK, [StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.LOCAL, [StateManagementTypes.LOCAL_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PARAM, [StateManagementTypes.PARAM_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.ONCE, [StateManagementTypes.ONCE_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.PROVIDER, [StateManagementTypes.PROVIDER_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.CONSUMER, [StateManagementTypes.CONSUMER_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.COMPUTED, [StateManagementTypes.COMPUTED_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [DecoratorNames.MONITOR, [StateManagementTypes.MONITOR_DECORATED, StateManagementTypes.STATE_MANAGEMENT_FACTORY]], + [ + DecoratorNames.LOCAL_STORAGE_PROP, + [ + StateManagementTypes.STORAGE_LINK_STATE, + StateManagementTypes.SYNCED_PROPERTY, + StateManagementTypes.OBSERVABLE_PROXY, + StateManagementTypes.PROP_STATE, + ], + ], + [ + DecoratorNames.OBSERVED, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.OBSERVE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.TRACK, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.OBSERVE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.OBSERVED_V2, + [ + StateManagementTypes.MUTABLE_STATE_META, + StateManagementTypes.OBSERVED_OBJECT, + StateManagementTypes.WATCH_ID_TYPE, + StateManagementTypes.RENDER_ID_TYPE, + StateManagementTypes.SUBSCRIBED_WATCHES, + StateManagementTypes.STATE_MANAGEMENT_FACTORY + ], + ], + [ + DecoratorNames.TRACE, + [ + StateManagementTypes.STATE_MANAGEMENT_FACTORY, + StateManagementTypes.UI_UTILS + ], + ], + [DecoratorNames.ANIMATABLE_EXTEND, [AnimationNames.ANIMATABLE_ARITHMETIC]] ]); +/** + * @deprecated + */ +export const IMPORT_SOURCE_MAP_V2: Map = new Map([ + [Dollars.TRANSFORM_DOLLAR_RESOURCE, 'arkui.component.resources'], + [Dollars.TRANSFORM_DOLLAR_RAWFILE, 'arkui.component.resources'], + [StateManagementTypes.SYNCED_PROPERTY, 'arkui.stateManagement.runtime'], + [StateManagementTypes.STORAGE_LINK_STATE, 'arkui.stateManagement.runtime'], + [StateManagementTypes.OBSERVABLE_PROXY, 'arkui.stateManagement.runtime'], + [StateManagementTypes.PROP_STATE, 'arkui.stateManagement.runtime'], + [StateManagementTypes.UI_UTILS, 'arkui.stateManagement.utils'], + [AnimationNames.ANIMATABLE_ARITHMETIC, 'arkui.component.common'] +]); -export enum InteroperAbilityNames { - ARKTS_1_1 = '1.1', - ARKTS_1_2 = '1.2', - ARKUICOMPATIBLE = 'ArkUICompatible', - ESVALUE = 'ESValue', - ELMTID = 'elmtId', - INITEMPTYOBJECT = 'instantiateEmptyObject', - SETPROPERTY = 'setProperty', - NUMBER = 'number', - PARENT = 'parent', - INSTANCE = 'instance', - PARAM = 'param', - EXTRAINFO = 'extraInfo', - COMPONENT = 'component', - GETPROPERTY = 'getProperty', - CONSTRUCTOR = 'constructor', - MODULE = 'module', - LOAD = 'load', - STRUCTOBJECT = 'structObject', - INSTANTIATE = 'instantiate', - WRAP = 'wrap', - WRAPINT = 'wrapInt', - WRAPSTRING = 'wrapString', - PARAMSLAMBDA = 'paramsLambda', - INTEROPCOMPONENT = 'interopComponent', - OHMURL = '@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0', +export enum GetSetTypes { + GET = 'get', + SET = 'set', } + +export enum GenSymPrefix { + INTRINSIC = 'gensym%%', + UI = 'gensym__' +} \ No newline at end of file diff --git a/arkui-plugins/common/program-visitor.ts b/arkui-plugins/common/program-visitor.ts index 9281c0b7808e368fcb14ef63429bc0321f0d1dc1..55ac1aad41e3b936d0d1faeeb5d037b98db90c66 100644 --- a/arkui-plugins/common/program-visitor.ts +++ b/arkui-plugins/common/program-visitor.ts @@ -17,9 +17,9 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor, VisitorOptions } from './abstract-visitor'; import { matchPrefix } from './arkts-utils'; import { debugDump, getDumpFileName } from './debug'; -import { InteroperAbilityNames, ARKUI_COMPONENT_IMPORT_NAME, KIT_ARKUI_NAME } from './predefines'; +import { InteroperAbilityNames } from '../ui-plugins/interop/predefines'; import { PluginContext } from './plugin-context'; -import { LegacyTransformer } from '../ui-plugins/legacy-transformer'; +import { LegacyTransformer } from '../ui-plugins/interop/legacy-transformer'; import { ComponentTransformer } from '../ui-plugins/component-transformer'; export interface ProgramVisitorOptions extends VisitorOptions { @@ -59,28 +59,9 @@ function flattenVisitorsInHooks( ]; } -function sortExternalSources(externalSources: arkts.ExternalSource[]): arkts.ExternalSource[] { - return externalSources.sort((a, b) => { - const prefix = ARKUI_COMPONENT_IMPORT_NAME || KIT_ARKUI_NAME; - const hasPrefixA = a.getName().startsWith(prefix); - const hasPrefixB = b.getName().startsWith(prefix); - - // If both have the prefix, maintain their original order - if (hasPrefixA && hasPrefixB) { - return 0; - } - // If neither has the prefix, maintain their original order - if (!hasPrefixA && !hasPrefixB) { - return 0; - } - // If only one has the prefix, the one with the prefix comes first - return hasPrefixA ? -1 : 1; - }); -} - export interface StructMap { [key: string]: string; -} +} export class ProgramVisitor extends AbstractVisitor { private readonly pluginName: string; @@ -113,7 +94,7 @@ export class ProgramVisitor extends AbstractVisitor { this.legacyModuleList = []; } - getLegacyModule(): void { + private getLegacyModule(): void { const moduleList = this.pluginContext?.getProjectConfig()?.dependentModuleList; if (moduleList === undefined) { return; @@ -125,143 +106,158 @@ export class ProgramVisitor extends AbstractVisitor { continue; } if (!this.legacyStructMap.has(moduleName)) { - this.legacyStructMap.set(moduleName, {}); + this.legacyStructMap.set(moduleName, {}); this.legacyModuleList.push(moduleName); } } } - dumpHeaders( - currProgram: arkts.Program, + private dumpExternalSource( + script: arkts.AstNode, name: string, cachePath: string | undefined, prefixName: string, extensionName: string ): void { debugDump( - currProgram.astNode.dumpSrc(), - getDumpFileName(this.state, prefixName, undefined, name), + script.dumpSrc(), + getDumpFileName(this.state, prefixName, undefined, name), true, cachePath, extensionName ); } - programVisitor(program: arkts.Program): arkts.Program { - const skipPrefixes: (string | RegExp)[] = this.skipPrefixNames; + private visitLegacyInExternalSource(currProgram: arkts.Program, name: string): void { + if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { + const structList = this.visitorLegacy(currProgram.astNode, currProgram, name); + const moduleName = name.split('/')[0]; + const structMap = this.legacyStructMap.get(moduleName)!; + for (const struct of structList) { + structMap[struct] = name; + } + } + } + + private visitNonLegacyInExternalSource( + program: arkts.Program, + currProgram: arkts.Program, + name: string, + cachePath?: string + ): void { + const extensionName: string = program.fileNameWithExtension; + this.dumpExternalSource(currProgram.astNode, name, cachePath, 'ORI', extensionName); + const script = this.visitor(currProgram.astNode, currProgram, name); + if (script) { + this.dumpExternalSource(script, name, cachePath, this.pluginName, extensionName); + } + } + + private visitNextProgramInQueue( + queue: arkts.Program[], + visited: Set, + externalSource: arkts.ExternalSource + ): void { + const nextProgramArr: arkts.Program[] = externalSource.programs ?? []; + for (const nextProgram of nextProgramArr) { + this.filenames.set(nextProgram.peer, externalSource.getName()); + if (!visited.has(nextProgram.peer)) { + queue.push(nextProgram); + } + } + } + + private visitExternalSources( + program: arkts.Program, + programQueue: arkts.Program[] + ): void { const visited = new Set(); - const queue: arkts.Program[] = [program]; + const queue: arkts.Program[] = programQueue; this.getLegacyModule(); while (queue.length > 0) { const currProgram = queue.shift()!; - if (visited.has(currProgram.peer)) { + if (visited.has(currProgram.peer) || currProgram.isASTLowered()) { continue; } if (currProgram.peer !== program.peer) { const name: string = this.filenames.get(currProgram.peer)!; const cachePath: string | undefined = this.pluginContext?.getProjectConfig()?.cachePath; if (this.legacyModuleList && matchPrefix(this.legacyModuleList, name)) { - if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { - const structList = this.visitorLegacy(currProgram.astNode, currProgram, name); - const moduleName = name.split('/')[0]; - const structMap = this.legacyStructMap.get(moduleName)!; - for (const struct of structList) { - structMap[struct] = name; - } - } + this.visitLegacyInExternalSource(currProgram, name); } else { - this.dumpHeaders(currProgram, name, cachePath, 'Ori', program.programFileNameWithExtension); - const script = this.visitor(currProgram.astNode, currProgram, name); - if (script) { - this.dumpHeaders(currProgram, name, cachePath, this.pluginName, program.programFileNameWithExtension); - } + this.visitNonLegacyInExternalSource(program, currProgram, name, cachePath); } } visited.add(currProgram.peer); - for (const externalSource of sortExternalSources(currProgram.externalSources)) { - if (matchPrefix(skipPrefixes, externalSource.getName())) { + for (const externalSource of currProgram.externalSources) { + if (matchPrefix(this.skipPrefixNames, externalSource.getName())) { continue; } - const nextProgramArr: arkts.Program[] = externalSource.programs ?? []; - for (const nextProgram of nextProgramArr) { - this.filenames.set(nextProgram.peer, externalSource.getName()); - if (!visited.has(nextProgram.peer)) { - queue.push(nextProgram); - } - } + this.visitNextProgramInQueue(queue, visited, externalSource); } } - const hasLegacy = this.legacyStructMap.size ? true : false; + } + + programVisitor(program: arkts.Program): arkts.Program { + this.visitExternalSources(program, [program]); + let programScript = program.astNode; - programScript = this.visitor(programScript, program, this.externalSourceName, hasLegacy); + programScript = this.visitor(programScript, program, this.externalSourceName); + const visitorsToReset = flattenVisitorsInHooks(this.hooks, this.state); visitorsToReset.forEach((visitor) => visitor.reset()); + return program; } - preVisitor( + private preVisitor( + hook: ProgramHookLifeCycle | undefined, node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string ): void { - const isExternal: boolean = !!externalSourceName; - let hook: ProgramHookLifeCycle | undefined = isExternal ? this.hooks?.external : this.hooks?.source; let script: arkts.EtsScript = node as arkts.EtsScript; - const preVisitors = hook?.pre?.visitors ?? []; for (const transformer of preVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); + this.visitTransformer(transformer, script, externalSourceName, program); if (!this.hooks?.external?.pre?.resetAfter) { transformer.reset(); } } } - postVisitor( + private postVisitor( + hook: ProgramHookLifeCycle | undefined, node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string ): void { - const isExternal: boolean = !!externalSourceName; - let hook: ProgramHookLifeCycle | undefined = isExternal ? this.hooks?.external : this.hooks?.source; let script: arkts.EtsScript = node as arkts.EtsScript; - const postVisitors = hook?.post?.visitors ?? []; for (const transformer of postVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); + this.visitTransformer(transformer, script, externalSourceName, program); if (!this.hooks?.external?.pre?.resetAfter) { transformer.reset(); } } } - visitor( - node: arkts.AstNode, - program?: arkts.Program, - externalSourceName?: string, - hasLegacy: boolean = false - ): arkts.EtsScript { + visitor(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): arkts.EtsScript { + let hook: ProgramHookLifeCycle | undefined; + let script: arkts.EtsScript = node as arkts.EtsScript; let count: number = 0; const isExternal: boolean = !!externalSourceName; // pre-run visitors - this.preVisitor(node, program, externalSourceName); + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.preVisitor(hook, node, program, externalSourceName); for (const transformer of this.visitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - if (hasLegacy && transformer instanceof ComponentTransformer) { + if (this.legacyStructMap.size > 0 && transformer instanceof ComponentTransformer) { transformer.registerMap(this.legacyStructMap); } - script = transformer.visitor(script) as arkts.EtsScript; + this.visitTransformer(transformer, script, externalSourceName, program); transformer.reset(); arkts.setAllParents(script); if (!transformer.isExternal) { @@ -270,26 +266,38 @@ export class ProgramVisitor extends AbstractVisitor { getDumpFileName(this.state, this.pluginName, count, transformer.constructor.name), true, this.pluginContext?.getProjectConfig()?.cachePath, - program!.programFileNameWithExtension + program!.fileNameWithExtension ); count += 1; } } // post-run visitors - this.postVisitor(node, program, externalSourceName); - + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.postVisitor(hook, node, program, externalSourceName); return script; } - visitorLegacy( - node: arkts.AstNode, - program?: arkts.Program, - externalSourceName?: string, - ): string[] { - const visitor = new LegacyTransformer(); - const script = visitor.visitor(node) as arkts.EtsScript; - const structList = visitor.getList(); + private visitorLegacy(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): string[] { + const transformer = new LegacyTransformer(); + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + transformer.visitor(node); + const structList = transformer.getList(); return structList; } + + private visitTransformer( + transformer: AbstractVisitor, + script: arkts.EtsScript, + externalSourceName?: string, + program?: arkts.Program + ): arkts.EtsScript { + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + const newScript = transformer.visitor(script) as arkts.EtsScript; + return newScript; + } } diff --git a/arkui-plugins/common/safe-types.ts b/arkui-plugins/common/safe-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..87345cf196a2952101a3b802924e95b6d3991503 --- /dev/null +++ b/arkui-plugins/common/safe-types.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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'; + +export type PartialExcept = Partial & Pick; + +type PartialArray = T extends readonly any[] | any[] ? T | undefined : T; +type PartialAstNode = T extends arkts.AstNode ? T | undefined : T; +type PartialObject = T extends object ? { [P in keyof T]?: T[P] } : T; +type PartialPrimitive = T; + +export type PartialNested = { + [P in keyof T]?: T[P] extends readonly any[] | any[] + ? PartialArray + : T[P] extends arkts.AstNode + ? PartialAstNode + : T[P] extends object + ? PartialObject + : PartialPrimitive; +}; + +type NestedKey = { + [P in keyof T]: P extends K ? T[P] : T[P] extends object ? NestedKey : T[P]; + }; + +export type PickNested = { + [P in keyof T]: P extends K ? T[P] : T[P] extends object ? NestedKey : T[P]; +}; + +export type PartialNestedExcept = PartialNested> & PickNested; + +export type AstNodePointer = arkts.AstNode['peer']; \ No newline at end of file diff --git a/arkui-plugins/interop-plugins/decl_transformer.ts b/arkui-plugins/interop-plugins/decl_transformer.ts index 58d5a6a6dcad76f8c71bb8cdd6aa3ea2aafcdade..32d6a610ed38b6c00179a12eb7fe035cec5f6988 100644 --- a/arkui-plugins/interop-plugins/decl_transformer.ts +++ b/arkui-plugins/interop-plugins/decl_transformer.ts @@ -69,6 +69,8 @@ export class DeclTransformer extends AbstractVisitor { return this.transformMethodDefinition(astNode); } return astNode; + } else if (arkts.isClassProperty(astNode)) { + astNode.setAnnotations([]); } return node; } @@ -119,7 +121,7 @@ export class DeclTransformer extends AbstractVisitor { } updateImportDeclaration(astNode: arkts.AstNode):arkts.AstNode { - if (!arkts.isETSImportDeclaration(astNode) || astNode?.source?.str !== '@ohos.arkui.component') { + if (!arkts.isETSImportDeclaration(astNode) || (astNode?.source?.str !== '@ohos.arkui.component' && astNode?.source?.str !== '@ohos.arkui.stateManagement')) { return astNode; } astNode.specifiers.forEach((element) => { diff --git a/arkui-plugins/interop-plugins/index.ts b/arkui-plugins/interop-plugins/index.ts index 841f74e11e37f16fd0a9e451c04a578d3ca4309f..dc69677c950a3dfd7cb75baca0361a07692407b1 100644 --- a/arkui-plugins/interop-plugins/index.ts +++ b/arkui-plugins/interop-plugins/index.ts @@ -88,7 +88,6 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance()?.reset(); arkts.recheckSubtree(script); this.setArkTSAst(script); debugLog('interopTransform:checked exit'); diff --git a/arkui-plugins/jest-test.config.js b/arkui-plugins/jest-test.config.js index 541b1dc3913d9c2621eb0c0a6408447351f747d1..cbca1f13e2e3c5cf964642ba71a4c025e339e706 100644 --- a/arkui-plugins/jest-test.config.js +++ b/arkui-plugins/jest-test.config.js @@ -19,6 +19,7 @@ const rootPath = path.resolve(__dirname, '../../../'); const sdkPath = path.resolve(rootPath, './out/sdk/ohos-sdk/linux/ets/ets1.2'); const pandaSdkPath = path.resolve(sdkPath, './build-tools/ets2panda'); const apiPath = path.resolve(sdkPath, './api'); +const kitPath = path.resolve(sdkPath, './kits'); module.exports = { testEnvironment: 'node', @@ -29,6 +30,7 @@ module.exports = { moduleFileExtensions: ['ts', 'js', 'json', 'node'], coverageDirectory: './test/report', collectCoverageFrom: [ + 'collectors/**', 'common/**', 'memo-plugins/**', 'ui-plugins/**' @@ -45,12 +47,13 @@ module.exports = { 'ui-plugins/printer-transformer.ts', 'ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts', 'ui-plugins/entry-translators/entry-transformer.ts', - 'ui-plugins/struct-translators/struct-transformer.ts' + 'ui-plugins/struct-translators/struct-transformer.ts', ], verbose: true, globals: { SDK_PATH: sdkPath, PANDA_SDK_PATH: pandaSdkPath, API_PATH: apiPath, + KIT_PATH: kitPath, }, }; diff --git a/arkui-plugins/memo-plugins/function-transformer.ts b/arkui-plugins/memo-plugins/function-transformer.ts index 68bc9083d22f33b37ad2155835e007497072497e..4f2c55f1f4c572c8cabfa2ede178bc0d0cc1018d 100644 --- a/arkui-plugins/memo-plugins/function-transformer.ts +++ b/arkui-plugins/memo-plugins/function-transformer.ts @@ -48,13 +48,14 @@ import { isStandaloneArrowFunction, isThisAttributeAssignment, removeMemoAnnotation, - parametrizedNodeHasReceiver + parametrizedNodeHasReceiver, } from './utils'; import { ParameterTransformer } from './parameter-transformer'; import { ReturnTransformer } from './return-transformer'; import { SignatureTransformer } from './signature-transformer'; import { moveToFront } from '../common/arkts-utils'; import { InternalsTransformer } from './internal-transformer'; +import { CachedMetadata, rewriteByType } from './memo-cache-factory'; interface ScopeInfo extends MemoInfo { regardAsSameScope?: boolean; @@ -66,6 +67,7 @@ export interface FunctionTransformerOptions extends VisitorOptions { returnTransformer: ReturnTransformer; signatureTransformer: SignatureTransformer; internalsTransformer?: InternalsTransformer; + useCache?: boolean; } export class FunctionTransformer extends AbstractVisitor { @@ -74,6 +76,7 @@ export class FunctionTransformer extends AbstractVisitor { private readonly returnTransformer: ReturnTransformer; private readonly signatureTransformer: SignatureTransformer; private readonly internalsTransformer?: InternalsTransformer; + private readonly useCache: boolean; /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ private modified = false; @@ -85,6 +88,7 @@ export class FunctionTransformer extends AbstractVisitor { this.returnTransformer = options.returnTransformer; this.signatureTransformer = options.signatureTransformer; this.internalsTransformer = options.internalsTransformer; + this.useCache = !!options.useCache; } private scopes: ScopeInfo[] = []; @@ -380,12 +384,13 @@ export class FunctionTransformer extends AbstractVisitor { this.signatureTransformer.visitor(node.expression.scriptFunction) ); } + const that = this; const updatedArguments: arkts.AstNode[] = node.arguments.map((it) => { if (arkts.isArrowFunctionExpression(it) && isMemoArrowFunction(it)) { - this.enterAnonymousScope(it.scriptFunction); - const res = this.updateScriptFunction(it.scriptFunction); - this.exitAnonymousScope(); - this.modified = true; + that.enterAnonymousScope(it.scriptFunction); + const res = that.updateScriptFunction(it.scriptFunction); + that.exitAnonymousScope(); + that.modified = true; return arkts.factory.updateArrowFunction(it, res); } return it; @@ -676,7 +681,26 @@ export class FunctionTransformer extends AbstractVisitor { ); } + private visitorWithCache(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.NodeCache.getInstance().has(node)) { + const value = arkts.NodeCache.getInstance().get(node)!; + if (rewriteByType.has(value.type)) { + this.modified = true; + const metadata: CachedMetadata = { ...value.metadata, internalsTransformer: this.internalsTransformer }; + return rewriteByType.get(value.type)!(node, metadata); + } + } + if (arkts.isEtsScript(node) && this.modified) { + factory.createContextTypesImportDeclaration(this.program); + } + return node; + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + if (this.useCache) { + return this.visitorWithCache(beforeChildren); + } this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); this.exit(beforeChildren); diff --git a/arkui-plugins/memo-plugins/index.ts b/arkui-plugins/memo-plugins/index.ts index beb7edff90b4d6edc82e0fc0f20eeb6eb34d7488..5c19bc8414083b125b060e9fa01acd1f62e8bbf6 100644 --- a/arkui-plugins/memo-plugins/index.ts +++ b/arkui-plugins/memo-plugins/index.ts @@ -37,20 +37,22 @@ export function unmemoizeTransform(): Plugins { function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { console.log('[MEMO PLUGIN] AFTER CHECKED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:Memo-AfterCheck'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; let script = program.astNode; debugLog('[BEFORE MEMO SCRIPT] script: ', script.dumpSrc()); const cachePath: string | undefined = this.getProjectConfig()?.cachePath; const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode; - const canSkipPhases = false; + const canSkipPhases = !isFrameworkMode && program.canSkipPhases(); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 5, 'MEMO_AfterCheck_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('memo-checked'); program = checkedProgramVisit(program, this, canSkipPhases, isFrameworkMode); @@ -62,13 +64,20 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 6, 'MEMO_AfterCheck_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); + + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:Memo-AfterCheck'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:Memo-AfterCheck'); + arkts.Performance.getInstance().startMemRecord('Node:ArkTS:Recheck'); arkts.Performance.getInstance().createEvent('memo-recheck'); arkts.recheckSubtree(script); arkts.Performance.getInstance().stopEvent('memo-recheck', true); - arkts.Performance.getInstance().clearAllEvents(); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('ArkTS:Recheck'); + arkts.Performance.getInstance().stopMemRecord('Node:ArkTS:Recheck'); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('UIPlugin:End'); console.log('[MEMO PLUGIN] AFTER CHECKED EXIT'); return script; } @@ -83,22 +92,24 @@ function checkedProgramVisit( isFrameworkMode: boolean = false ): arkts.Program { if (canSkipPhases) { - debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program?.moduleName); + debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); } else { - debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program?.moduleName); + debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); - const parameterTransformer = new ParameterTransformer({ - positionalIdTracker, - }); + const parameterTransformer = new ParameterTransformer({ positionalIdTracker }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); - const internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, - internalsTransformer + internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected(), }); const skipPrefixNames = isFrameworkMode ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK @@ -111,6 +122,7 @@ function checkedProgramVisit( pluginContext, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); } return program; -} \ No newline at end of file +} diff --git a/arkui-plugins/memo-plugins/internal-transformer.ts b/arkui-plugins/memo-plugins/internal-transformer.ts index 7419778b2b8c3acd75aba8708335ecc942ec2385..3541fb7c37fb8db20c34eb7171c37ed3cad20044 100644 --- a/arkui-plugins/memo-plugins/internal-transformer.ts +++ b/arkui-plugins/memo-plugins/internal-transformer.ts @@ -46,4 +46,4 @@ export class InternalsTransformer extends AbstractVisitor { } return node; } -} \ No newline at end of file +} diff --git a/arkui-plugins/memo-plugins/memo-cache-factory.ts b/arkui-plugins/memo-plugins/memo-cache-factory.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ca4234ed596b0ebecb6b28d244b19272ebb1d75 --- /dev/null +++ b/arkui-plugins/memo-plugins/memo-cache-factory.ts @@ -0,0 +1,430 @@ +/* + * 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 { factory } from './memo-factory'; +import { + filterMemoSkipParams, + findLocalReturnTypeFromTypeAnnotation, + findUnmemoizedScopeInFunctionBody, + fixGensymParams, + getFunctionParamsBeforeUnmemoized, + hasMemoAnnotation, + isVoidType, + mayAddLastReturn, + parametrizedNodeHasReceiver, + ParamInfo, + PositionalIdTracker, +} from './utils'; +import { InternalsTransformer } from './internal-transformer'; +import { GenSymPrefix } from '../common/predefines'; + +export interface CachedMetadata extends arkts.AstNodeCacheValueMetadata { + internalsTransformer?: InternalsTransformer; +} + +export class RewriteFactory { + static rewriteUnionType(node: arkts.ETSUnionType, metadata?: CachedMetadata): arkts.ETSUnionType { + return arkts.factory.updateUnionType( + node, + node.types.map((t) => { + if (arkts.isETSFunctionType(t)) { + return RewriteFactory.rewriteFunctionType(t, metadata); + } + if (arkts.isETSUnionType(t)) { + return RewriteFactory.rewriteUnionType(t, metadata); + } + return t; + }) + ); + } + + static rewriteFunctionType(node: arkts.ETSFunctionType, metadata?: CachedMetadata): arkts.ETSFunctionType { + const canRewriteType = !metadata?.forbidTypeRewrite; + const isWithinTypeParams = !!metadata?.isWithinTypeParams && hasMemoAnnotation(node); + if (!canRewriteType && !isWithinTypeParams) { + return node; + } + const hasReceiver = metadata?.hasReceiver ?? parametrizedNodeHasReceiver(node); + return factory.updateFunctionTypeWithMemoParameters(node, hasReceiver); + } + + static rewriteETSTypeReference(node: arkts.ETSTypeReference, metadata?: CachedMetadata): arkts.ETSTypeReference { + if (!metadata?.isWithinTypeParams) { + return node; + } + const part = node.part; + if (!part) { + return node; + } + const typeParams = part.typeParams; + if (!typeParams) { + return node; + } + const newTypeParams = arkts.factory.updateTSTypeParameterInstantiation( + typeParams, + typeParams.params.map((t) => RewriteFactory.rewriteType(t, metadata)!) + ); + return arkts.factory.updateTypeReference( + node, + arkts.factory.updateTypeReferencePart(part, part.name, newTypeParams, part.previous) + ); + } + + /** + * @internal + */ + static rewriteType(node: arkts.TypeNode | undefined, metadata?: CachedMetadata): arkts.TypeNode | undefined { + let newNodeType = node; + if (!!newNodeType && arkts.isETSFunctionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteFunctionType(newNodeType, metadata); + } else if (!!newNodeType && arkts.isETSUnionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteUnionType(newNodeType, metadata); + } else if (!!newNodeType && arkts.isETSTypeReference(newNodeType)) { + return RewriteFactory.rewriteETSTypeReference(newNodeType, metadata); + } + return newNodeType; + } + + static rewriteTypeAlias( + node: arkts.TSTypeAliasDeclaration, + metadata?: CachedMetadata + ): arkts.TSTypeAliasDeclaration { + if (!node.typeAnnotation) { + return node; + } + const newNodeType = RewriteFactory.rewriteType(node.typeAnnotation); + return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, newNodeType); + } + + static rewriteParameter( + node: arkts.ETSParameterExpression, + metadata?: CachedMetadata + ): arkts.ETSParameterExpression { + if (!node.type && !node.initializer) { + return node; + } + node.type = RewriteFactory.rewriteType(node.type as arkts.TypeNode, metadata); + return arkts.factory.updateParameterDeclaration(node, node.identifier, node.initializer); + } + + static rewriteProperty(node: arkts.Property, metadata?: CachedMetadata): arkts.Property { + if (!node.value || !arkts.isArrowFunctionExpression(node.value)) { + return node; + } + return arkts.factory.updateProperty(node, node.key, RewriteFactory.rewriteArrowFunction(node.value, metadata)); + } + + static rewriteClassProperty(node: arkts.ClassProperty, metadata?: CachedMetadata): arkts.ClassProperty { + const newType = !!node.typeAnnotation ? RewriteFactory.rewriteType(node.typeAnnotation, metadata) : undefined; + const newValue = + !!node.value && arkts.isArrowFunctionExpression(node.value) + ? RewriteFactory.rewriteArrowFunction(node.value, metadata) + : node.value; + return arkts.factory.updateClassProperty(node, node.key, newValue, newType, node.modifiers, node.isComputed); + } + + static rewriteArrowFunction( + node: arkts.ArrowFunctionExpression, + metadata?: arkts.AstNodeCacheValueMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ArrowFunctionExpression { + return arkts.factory.updateArrowFunction( + node, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, metadata, expectReturn) + ); + } + + /** + * @internal + */ + static rewriteScriptFunctionBody( + node: arkts.ScriptFunction, + body: arkts.BlockStatement, + positionalIdTracker: PositionalIdTracker, + callName?: string, + hasReceiver?: boolean, + expectReturn?: arkts.TypeNode + ): arkts.BlockStatement { + const _hasReceiver = hasReceiver ?? node.hasReceiver; + const _callName = callName ?? node.id?.name; + const parameters = getFunctionParamsBeforeUnmemoized(node.params, _hasReceiver); + const filteredParameters = filterMemoSkipParams(parameters); + const declaredParams: ParamInfo[] = filteredParameters.map((p) => { + const param = p as arkts.ETSParameterExpression; + return { ident: param.identifier, param }; + }); + const _gensymCount = fixGensymParams(declaredParams, body); + if (findUnmemoizedScopeInFunctionBody(body, _gensymCount)) { + return body; + } + const returnType = + node.returnTypeAnnotation ?? + expectReturn ?? + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + const _isVoidReturn = isVoidType(returnType); + const scopeDeclaration = factory.createScopeDeclaration( + returnType, + positionalIdTracker.id(_callName), + declaredParams.length + ); + const memoParametersDeclaration = node.params.length + ? factory.createMemoParameterDeclaration(declaredParams.map((p) => p.ident.name)) + : undefined; + const syntheticReturnStatement = factory.createSyntheticReturnStatement(false); + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( + syntheticReturnStatement, + _isVoidReturn + ); + const lastReturn = mayAddLastReturn(body) + ? factory.createWrappedReturnStatement(factory.createRecacheCall(), _isVoidReturn) + : undefined; + return arkts.factory.updateBlock(body, [ + ...body.statements.slice(0, _gensymCount), + scopeDeclaration, + ...(!!memoParametersDeclaration ? [memoParametersDeclaration] : []), + unchangedCheck, + ...body.statements.slice(_gensymCount), + ...(!!lastReturn ? [lastReturn] : []), + ]); + } + + static rewriteScriptFunction( + node: arkts.ScriptFunction, + metadata?: CachedMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ScriptFunction { + const _callName = metadata?.callName; + const _hasReceiver = metadata?.hasReceiver ?? node.hasReceiver; + const _isSetter = !!metadata?.isSetter; + const _isGetter = !!metadata?.isGetter; + const _hasMemoEntry = !!metadata?.hasMemoEntry; + const _hasMemoIntrinsic = !!metadata?.hasMemoIntrinsic; + const _internalsTransformer = metadata?.internalsTransformer; + const _isDecl = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const newParams: readonly arkts.Expression[] = prepareRewriteScriptFunctionParameters( + node, + _isSetter, + _isGetter, + _hasReceiver + ); + const newReturnType: arkts.TypeNode | undefined = prepareRewriteScriptFunctionReturnType( + node, + _isGetter, + _hasReceiver + ); + const newBody: arkts.AstNode | undefined = prepareRewriteScriptFunctionBody( + node, + expectReturn, + _internalsTransformer, + _isDecl, + _hasMemoEntry, + _hasMemoIntrinsic, + _callName, + _hasReceiver, + _isGetter, + _isSetter + ); + return arkts.factory.updateScriptFunction( + node, + newBody, + arkts.factory.createFunctionSignature(node.typeParams, newParams, newReturnType, _hasReceiver), + node.flags, + node.modifiers + ); + } + + static rewriteMethodDefinition(node: arkts.MethodDefinition, metadata?: CachedMetadata): arkts.MethodDefinition { + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (node.overloads.length > 0) { + node.setOverloads(node.overloads.map((o) => RewriteFactory.rewriteMethodDefinition(o, metadata))); + } + return arkts.factory.updateMethodDefinition( + node, + node.kind, + node.name, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, { + callName: node.name.name, + ...metadata, + isSetter, + isGetter, + }), + node.modifiers, + false + ); + } + + static rewriteCallExpression(node: arkts.CallExpression, metadata?: CachedMetadata): arkts.CallExpression { + const _hasMemoEntry = !!metadata?.hasMemoEntry; + if (_hasMemoEntry) { + return node; + } + const _hasReceiver = metadata?.hasReceiver; + let _callName: string | undefined = metadata?.callName; + if (!!_callName && arkts.isIdentifier(node.expression)) { + _callName = node.expression.name; + } else if ( + !!_callName && + arkts.isMemberExpression(node.expression) && + arkts.isIdentifier(node.expression.property) + ) { + _callName = node.expression.property.name; + } + return factory.insertHiddenArgumentsToCall( + node, + PositionalIdTracker.getInstance(arkts.getFileName()).id(_callName), + _hasReceiver + ); + } + + static rewriteIdentifier( + node: arkts.Identifier, + metadata?: CachedMetadata + ): arkts.Identifier | arkts.MemberExpression { + if (!node.name.startsWith(GenSymPrefix.INTRINSIC) && !node.name.startsWith(GenSymPrefix.UI)) { + return factory.createMemoParameterAccess(node.name); + } + return node; + } + + static rewriteReturnStatement( + node: arkts.ReturnStatement, + metadata?: CachedMetadata + ): arkts.ReturnStatement | arkts.BlockStatement { + return factory.createWrappedReturnStatement(factory.createRecacheCall(node.argument), !node.argument); + } + + static rewriteVariableDeclarator( + node: arkts.VariableDeclarator, + metadata?: CachedMetadata + ): arkts.VariableDeclarator { + const expectReturnType = findLocalReturnTypeFromTypeAnnotation(node.name.typeAnnotation); + const variableType = RewriteFactory.rewriteType(node.name.typeAnnotation); + let initializer = node.initializer; + if (!!initializer && arkts.isConditionalExpression(initializer) && !!initializer.alternate) { + let alternate = initializer.alternate; + if (arkts.isTSAsExpression(alternate)) { + alternate = arkts.factory.updateTSAsExpression( + alternate, + !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr) + ? RewriteFactory.rewriteArrowFunction(alternate.expr, metadata, expectReturnType) + : alternate.expr, + RewriteFactory.rewriteType(alternate.typeAnnotation), + alternate.isConst + ); + } else if (arkts.isArrowFunctionExpression(alternate)) { + alternate = RewriteFactory.rewriteArrowFunction(alternate, metadata, expectReturnType); + } + initializer = arkts.factory.updateConditionalExpression( + initializer, + initializer.test, + initializer.consequent, + alternate + ); + } else if (!!initializer && arkts.isArrowFunctionExpression(initializer)) { + initializer = RewriteFactory.rewriteArrowFunction(initializer, metadata, expectReturnType); + } + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier(node.name, node.name.name, variableType), + initializer + ); + } +} + +function prepareRewriteScriptFunctionParameters( + node: arkts.ScriptFunction, + isSetter?: boolean, + isGetter?: boolean, + hasReceiver?: boolean +): readonly arkts.Expression[] { + let newParams: readonly arkts.Expression[] = node.params; + if (!isSetter && !isGetter) { + newParams = factory.createHiddenParameterIfNotAdded(node.params, node.hasReceiver); + } else if (isSetter && node.params.length > 0) { + if (hasReceiver && node.params.length === 2) { + newParams = [ + node.params.at(0)!, + RewriteFactory.rewriteParameter(node.params.at(1)! as arkts.ETSParameterExpression), + ]; + } else { + newParams = [RewriteFactory.rewriteParameter(node.params.at(0)! as arkts.ETSParameterExpression)]; + } + } + return newParams; +} + +function prepareRewriteScriptFunctionReturnType( + node: arkts.ScriptFunction, + isGetter?: boolean, + hasReceiver?: boolean +): arkts.TypeNode | undefined { + let newReturnType: arkts.TypeNode | undefined = node.returnTypeAnnotation; + if (!!node.returnTypeAnnotation && isGetter) { + newReturnType = RewriteFactory.rewriteType(node.returnTypeAnnotation, { hasReceiver }); + } + return newReturnType; +} + +function prepareRewriteScriptFunctionBody( + node: arkts.ScriptFunction, + expectReturn?: arkts.TypeNode, + internalsTransformer?: InternalsTransformer, + isDecl?: boolean, + hasMemoEntry?: boolean, + hasMemoIntrinsic?: boolean, + callName?: string, + hasReceiver?: boolean, + isGetter?: boolean, + isSetter?: boolean +): arkts.AstNode | undefined { + if (isGetter || isSetter || isDecl || !node.body || !arkts.isBlockStatement(node.body)) { + return node.body; + } + + let newBody: arkts.AstNode | undefined; + const positionalIdTracker = PositionalIdTracker.getInstance(arkts.getFileName()); + newBody = internalsTransformer?.visitor(node.body) ?? node.body; + if (!hasMemoEntry && !hasMemoIntrinsic) { + newBody = RewriteFactory.rewriteScriptFunctionBody( + node, + newBody as arkts.BlockStatement, + positionalIdTracker, + callName, + hasReceiver, + expectReturn + ); + } + return newBody; +} + +export const rewriteByType = new Map arkts.AstNode>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, RewriteFactory.rewriteUnionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, RewriteFactory.rewriteFunctionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE, RewriteFactory.rewriteETSTypeReference], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, RewriteFactory.rewriteTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, RewriteFactory.rewriteParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, RewriteFactory.rewriteClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, RewriteFactory.rewriteArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, RewriteFactory.rewriteScriptFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, RewriteFactory.rewriteMethodDefinition], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, RewriteFactory.rewriteCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, RewriteFactory.rewriteIdentifier], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, RewriteFactory.rewriteReturnStatement], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR, RewriteFactory.rewriteVariableDeclarator], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, RewriteFactory.rewriteProperty], +]); diff --git a/arkui-plugins/memo-plugins/memo-factory.ts b/arkui-plugins/memo-plugins/memo-factory.ts index dac88684374e48b3817e50afb24106ae8218d262..948fb082ebea21ea33a76a2652465c111505dada 100644 --- a/arkui-plugins/memo-plugins/memo-factory.ts +++ b/arkui-plugins/memo-plugins/memo-factory.ts @@ -17,12 +17,12 @@ import * as arkts from '@koalaui/libarkts'; import { fixGensymParams, buildeParamInfos, - isUnmemoizedInFunction, + isUnmemoizedInFunctionParams, mayAddLastReturn, ParamInfo, ReturnTypeInfo, RuntimeNames, - parametrizedNodeHasReceiver + parametrizedNodeHasReceiver, } from './utils'; import { moveToFront } from '../common/arkts-utils'; @@ -40,7 +40,6 @@ export class factory { arkts.factory.createIdentifier(RuntimeNames.ID_TYPE) ); } - static createContextTypesImportDeclaration(program?: arkts.Program): void { const source: arkts.StringLiteral = arkts.factory.createStringLiteral(RuntimeNames.MEMO_IMPORT_NAME); const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( @@ -89,7 +88,7 @@ export class factory { hasReceiver: boolean = false ): readonly arkts.Expression[] { const _params = params ?? []; - if (isUnmemoizedInFunction(_params)) { + if (isUnmemoizedInFunctionParams(_params)) { return _params; } let newParams: arkts.Expression[] = [...factory.createHiddenParameters(), ..._params]; @@ -98,12 +97,16 @@ export class factory { } return newParams; } - static updateFunctionTypeWithMemoParameters(type: arkts.ETSFunctionType): arkts.ETSFunctionType { + static updateFunctionTypeWithMemoParameters( + type: arkts.ETSFunctionType, + hasReceiver: boolean = false, + newTypeParams?: arkts.TSTypeParameterDeclaration + ): arkts.ETSFunctionType { return arkts.factory.updateFunctionType( type, arkts.factory.createFunctionSignature( - undefined, - factory.createHiddenParameterIfNotAdded(type.params), + newTypeParams, + factory.createHiddenParameterIfNotAdded(type.params, hasReceiver), type.returnType, false ), @@ -299,6 +302,18 @@ export class factory { returnStatement ); } + static createWrappedReturnStatement( + argument: arkts.Expression, + isReturnVoid: boolean + ): arkts.ReturnStatement | arkts.BlockStatement { + if (!isReturnVoid) { + return arkts.factory.createReturnStatement(argument); + } + return arkts.factory.createBlock([ + arkts.factory.createExpressionStatement(argument), + arkts.factory.createReturnStatement(), + ]); + } // Compute static createLambdaWrapper(node: arkts.Expression): arkts.ArrowFunctionExpression { @@ -390,11 +405,13 @@ export class factory { static insertHiddenArgumentsToCall( node: arkts.CallExpression, - hash: arkts.NumberLiteral | arkts.StringLiteral + hash: arkts.NumberLiteral | arkts.StringLiteral, + hasReceiver?: boolean ): arkts.CallExpression { - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - ...factory.createHiddenArguments(hash), - ...node.arguments, - ]); + let updatedArguments = [...factory.createHiddenArguments(hash), ...node.arguments]; + if (!!hasReceiver) { + updatedArguments = moveToFront(updatedArguments, 2); + } + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments); } } diff --git a/arkui-plugins/memo-plugins/parameter-transformer.ts b/arkui-plugins/memo-plugins/parameter-transformer.ts index 050333da7749d6da1d5fb8aca1a31c7f47fc85e8..1947b60d1a5032d00084f336dbd9a38c4590979e 100644 --- a/arkui-plugins/memo-plugins/parameter-transformer.ts +++ b/arkui-plugins/memo-plugins/parameter-transformer.ts @@ -22,8 +22,7 @@ import { findReturnTypeFromTypeAnnotation, isMemoETSParameterExpression, isMemoParametersDeclaration, - isUnmemoizedInFunction, - isVoidType, + isUnmemoizedInFunctionParams, MemoInfo, ParamInfo, PositionalIdTracker, @@ -140,7 +139,7 @@ export class ParameterTransformer extends AbstractVisitor { if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { return initializer; } - if (isUnmemoizedInFunction(scriptFunction.params)) { + if (isUnmemoizedInFunctionParams(scriptFunction.params)) { return initializer; } const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( diff --git a/arkui-plugins/memo-plugins/signature-transformer.ts b/arkui-plugins/memo-plugins/signature-transformer.ts index b7fa8a62d37b9500e7e334a9cf66ee816279e993..6c15cce2ddbb03b30833c632b4b4edeaa8229772 100644 --- a/arkui-plugins/memo-plugins/signature-transformer.ts +++ b/arkui-plugins/memo-plugins/signature-transformer.ts @@ -129,8 +129,7 @@ export class SignatureTransformer extends AbstractVisitor { const expr = node.part.name; const decl = arkts.getDecl(expr); if (!decl || !arkts.isTSTypeAliasDeclaration(decl)) { - console.error(`@memo parameter's type has not been declared`); - throw 'Invalid @memo usage'; + return node as any as T; } const memoDecl = isMemoTSTypeAliasDeclaration(decl); if (memoDecl) { diff --git a/arkui-plugins/memo-plugins/utils.ts b/arkui-plugins/memo-plugins/utils.ts index 2e193d099085ca1e0cf1c7c70a3cbca44bebfc5b..e3b415e7fa2216438caddbf64d73efd2ed1455d7 100644 --- a/arkui-plugins/memo-plugins/utils.ts +++ b/arkui-plugins/memo-plugins/utils.ts @@ -71,6 +71,15 @@ export class PositionalIdTracker { // Global for the whole program. static callCount: number = 0; + private static instance: PositionalIdTracker; + + static getInstance(fileName: string): PositionalIdTracker { + if (!this.instance) { + this.instance = new PositionalIdTracker(fileName); + } + return this.instance; + } + // Set `stable` to true if you want to have more predictable values. // For example for tests. // Don't use it in production! @@ -494,6 +503,21 @@ export function findReturnTypeFromTypeAnnotation( return undefined; } +export function findLocalReturnTypeFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): arkts.TypeNode | undefined { + if (!typeAnnotation) { + return undefined; + } + if (arkts.isETSFunctionType(typeAnnotation)) { + return typeAnnotation.returnType; + } + if (arkts.isETSUnionType(typeAnnotation)) { + return typeAnnotation.types.find((type) => arkts.isETSFunctionType(type))?.returnType; + } + return undefined; +} + export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { const decl = arkts.getDecl(node); if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { @@ -508,11 +532,17 @@ export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefi } export function mayAddLastReturn(node: arkts.BlockStatement): boolean { - return ( - node.statements.length === 0 || - (!arkts.isReturnStatement(node.statements[node.statements.length - 1]) && - !arkts.isThrowStatement(node.statements[node.statements.length - 1])) - ); + if (node.statements.length === 0) { + return true; + } + const lastStatement = node.statements[node.statements.length - 1]; + if (arkts.isBlockStatement(lastStatement)) { + return mayAddLastReturn(lastStatement); + } + if (arkts.isReturnStatement(lastStatement) || arkts.isThrowStatement(lastStatement)) { + return false; + } + return true; } export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement): number { @@ -536,16 +566,49 @@ export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement) return gensymParamsCount; } -export function isUnmemoizedInFunction(params?: readonly arkts.Expression[]): boolean { +export function isMemoContextParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.CONTEXT; +} + +export function isMemoIdParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.ID; +} + +export function isUnmemoizedInFunctionParams(params?: readonly arkts.Expression[], hasReceiver?: boolean): boolean { const _params = params ?? []; - const first = _params.at(0); - const isContextAdded = - !!first && arkts.isEtsParameterExpression(first) && first.identifier.name === RuntimeNames.CONTEXT; - const second = _params.at(1); - const isIdAdded = !!second && arkts.isEtsParameterExpression(second) && second.identifier.name === RuntimeNames.ID; + const startIndex = hasReceiver ? 1 : 0; + const isContextAdded = !!_params.at(startIndex) && isMemoContextParamAdded(_params.at(startIndex)!); + const isIdAdded = !!_params.at(startIndex + 1) && isMemoIdParamAdded(_params.at(startIndex + 1)!); return isContextAdded && isIdAdded; } +export function getFunctionParamsBeforeUnmemoized( + params?: readonly arkts.Expression[], + hasReceiver?: boolean +): readonly arkts.Expression[] { + const _params = params ?? []; + if (isUnmemoizedInFunctionParams(_params, hasReceiver)) { + if (!!hasReceiver) { + return [_params.at(0)!, ..._params.slice(3)]; + } + return _params.slice(2); + } + return _params; +} + +export function findUnmemoizedScopeInFunctionBody(body: arkts.BlockStatement, gensymCount: number = 0): boolean { + const startIndex = gensymCount; + if (body.statements.length < startIndex + 1) { + return false; + } + const statement = body.statements.at(startIndex)!; + if (!arkts.isVariableDeclaration(statement)) { + return false; + } + const declarator = statement.declarators.at(0)!; + return declarator.name.name === RuntimeNames.SCOPE; +} + export function buildReturnTypeInfo( returnType: arkts.TypeNode | undefined, isMemo?: boolean, @@ -589,3 +652,7 @@ function isThisParam(node: arkts.Expression | undefined): boolean { } return node.identifier?.isReceiver ?? false; } + +export function filterMemoSkipParams(params: readonly arkts.Expression[]): readonly arkts.Expression[] { + return params.filter((p) => !hasMemoSkipAnnotation(p as arkts.ETSParameterExpression)); +} diff --git a/arkui-plugins/npm_preinstall.sh b/arkui-plugins/npm_preinstall.sh deleted file mode 100755 index 478471ab0ddcfcec949a6753fc1dfd15dda9394b..0000000000000000000000000000000000000000 --- a/arkui-plugins/npm_preinstall.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# coding: utf-8 -# Copyright (c) 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. - -if [ "$1" == "--init" ]; then - cd ../koala-wrapper - npm install - npm run compile - npm link - - cd ../ts_wrapper - npm install - npm run compile - npm link - - cd ../arkui-plugins - mkdir node_modules - npm link @koalaui/libarkts -fi \ No newline at end of file diff --git a/arkui-plugins/package-lock.json b/arkui-plugins/package-lock.json deleted file mode 100644 index 27a190aac321d89de5561db74b7c801513e69a53..0000000000000000000000000000000000000000 --- a/arkui-plugins/package-lock.json +++ /dev/null @@ -1,5590 +0,0 @@ -{ - "name": "arkui-plugin", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/core": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/environment": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - } - }, - "@jest/expect": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "@jest/globals": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - } - }, - "@jest/reporters": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/transform": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@koalaui/libarkts": { - "version": "file:../koala-wrapper", - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==" - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "optional": true - }, - "@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==" - }, - "@types/node": { - "version": "18.19.119", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "optional": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==" - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "requires": { - "browserslist": "^4.25.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "requires": { - "ms": "^2.1.3" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==" - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "optional": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "optional": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node-addon-api": { - "version": "8.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==" - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" - } - } - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==" - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.14", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/node": { - "version": "22.16.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-22.16.4.tgz", - "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", - "dev": true, - "requires": { - "undici-types": "~6.21.0" - } - }, - "@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.33", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "acorn": { - "version": "8.15.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true - }, - "acorn-walk": { - "version": "8.3.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "requires": { - "acorn": "^8.11.0" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "async": { - "version": "3.2.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, - "babel-jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "dev": true, - "requires": { - "browserslist": "^4.25.1" - } - }, - "create-jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "dedent": { - "version": "1.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true - }, - "ejs": { - "version": "3.1.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "import-local": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "requires": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "dependencies": { - "@babel/core": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.9.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - } - }, - "jest": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "dependencies": { - "jest-cli": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } - } - } - }, - "jest-changed-files": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - } - } - }, - "jest-circus": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-config": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-diff": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-docblock": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - } - }, - "jest-environment-node": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-message-util": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-mock": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true - }, - "jest-regex-util": { - "version": "29.6.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - } - } - }, - "jest-runtime": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - } - } - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pirates": { - "version": "4.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "pretty-format": { - "version": "29.7.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "pure-rand": { - "version": "6.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true - }, - "react-is": { - "version": "18.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-jest": { - "version": "29.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", - "dev": true, - "requires": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "semver": { - "version": "7.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true - }, - "type-fest": { - "version": "4.41.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true - } - } - }, - "ts-node": { - "version": "10.9.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true - }, - "undici-types": { - "version": "6.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - } - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/arkui-plugins/package.json b/arkui-plugins/package.json index 0267fb00dffdd82d8637dfb6471e70e6b883f042..9f115edfeab10ae8a21a396c94fdeaa2862208ca 100644 --- a/arkui-plugins/package.json +++ b/arkui-plugins/package.json @@ -7,12 +7,12 @@ "./test" ], "scripts": { - "local:install": "chmod 777 ./npm_preinstall.sh && ./npm_preinstall.sh --init", "compile:plugins": "./node_modules/.bin/babel . --out-dir lib --extensions .ts", "compile:clean": "rm -rf lib", "clean:test": "rm -rf dist && rm -rf coverage", "prepare:test": "cp -rf $INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/koala-wrapper/build/native/ ../koala-wrapper/build/", - "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --silent", + "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --maxWorkers=50% --silent", + "test:gdb": "LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib gdb --args node ./node_modules/.bin/jest --config ./jest-test.config.js", "compile": "npm run compile:clean && npm run compile:plugins && cp -rf ./lib $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/" }, "devDependencies": { diff --git a/arkui-plugins/test/demo/interop/builder_interop.ets b/arkui-plugins/test/demo/interop/builder_interop.ets new file mode 100644 index 0000000000000000000000000000000000000000..b50fe49a380cc75c8efc9dea787e8c460ab9f47e --- /dev/null +++ b/arkui-plugins/test/demo/interop/builder_interop.ets @@ -0,0 +1,114 @@ +/* + * Copyright (C) 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. + */ + + +// ArkTS1.2 +'use static' +import { Entry, Text, Column, Component, Button, ClickEvent } from '@ohos.arkui.component' +import { State } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' +import { demoBuilder1_1, aa } from 'har1' + +@Entry +@Component +struct MyStateSample { + @State stateVar: string = 'state var'; + message: string = 'var'; + + build() { + Column(undefined) { + Text('Hello World').fontSize(20) + Button(this.message).backgroundColor('#FFFF00FF') + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + }) + Text(this.stateVar).fontSize(20) + demoBuilder1_1({ a:"a23", b:this.stateVar}) + let tmp:aa = { a:"a23", b:this.stateVar} + demoBuilder1_1(tmp) + } + } +} + + +//ArkT1.1 +@Builder +export function demoBuilder1_1( param1:aa) { + +} +export class aa { + a:string = '' + b:string = '' +} + + +//transform 1.1 Builder to compatibleComponent + +compatibleComponent((() => { + let global = ESValue.getGlobal(); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let createCompatibleNode = global.getProperty("createCompatibleNodeWithFunc"); + let paramObject0 = ESValue.instantiateEmptyObject(); + paramObject0.setProperty("a", ESValue.wrap("a23")); + paramObject0.setProperty("b", ESValue.wrap(this.stateVar)); + let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, paramObject0); + let viewPUCreate = global.getProperty("viewPUCreate"); + viewPUCreate.invoke(component); + return { + component: component, + name: "demoBuilder1_1", + }; +}), ((instance: ESValue) => { + let param = instance.getProperty("arg1"); + param.setProperty("a", ESValue.wrap("a23")); + param.setProperty("b", ESValue.wrap(this.stateVar)); + let global = ESValue.getGlobal(); + let runPendingJobs = global.getProperty("runPendingJobs"); + runPendingJobs.invoke(); +})); + +compatibleComponent((() => { + let global = ESValue.getGlobal(); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let createCompatibleNode = global.getProperty("createCompatibleNodeWithFunc"); + let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, ESValue.wrap(tmp)); + let viewPUCreate = global.getProperty("viewPUCreate"); + viewPUCreate.invoke(component); + return { + component: component, + name: "demoBuilder1_1", + }; +}), ((instance: ESValue) => { + let instanceParam = (instance.getProperty("arg1") as ESValue); + if (((instanceParam.typeOf()) != ("object"))) { + return; + } + let param_wrapped = ESValue.wrap(tmp); + let param_wrapped_it = param_wrapped.keys(); + while (true) { + let param_wrapped_key = param_wrapped_it.next(); + if (param_wrapped_key.done) { + break; + } + instanceParam.setProperty((param_wrapped_key.value as ESValue), param_wrapped.getProperty((param_wrapped_key.value as ESValue))); + } + let global = ESValue.getGlobal(); + let runPendingJobs = global.getProperty("runPendingJobs"); + runPendingJobs.invoke(); +})); diff --git a/arkui-plugins/test/demo/interop/link_link_interop.ets b/arkui-plugins/test/demo/interop/link_link_interop.ets new file mode 100644 index 0000000000000000000000000000000000000000..e5d5f8b47edd55d7d01c159e17e8b08ce229b646 --- /dev/null +++ b/arkui-plugins/test/demo/interop/link_link_interop.ets @@ -0,0 +1,118 @@ +/* + * Copyright (C) 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. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @State stateVar: new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child({stateVar: this.stateVar}) + } + } +} + +@Component +struct Child { + @Link stateVar; + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1({stateVar: this.stateVar, text: this.stateVar}) + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Link stateVar: MyText; + @Link text: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + Button(this.text.text) + .onClick(() => { + this.text.text = 'ArkTS1.1'; + }) + } + } +} + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + let stateVar_SetSource = ((value: MyText) => { + (this).stateVar = value; + }); + let stateVar_ProxyState = createState.invoke(ESValue.wrap((this).stateVar), ESValue.wrap(stateVar_SetSource)); + (this).__backing_stateVar!.setProxy(stateVar_ProxyState); + let stateVar_SetProxy = ((value: MyText) => { + stateVar_ProxyState.invokeMethod("set", ESValue.wrap(value)); + }); + (this).__backing_stateVar!.setProxyValue = stateVar_SetProxy; + let stateVar_NotifyCallback = ((propertyName: string) => { + stateVar_ProxyState.invokeMethod("notifyPropertyHasChangedPU"); + }); + (this).__backing_stateVar!.setNotifyCallback(stateVar_NotifyCallback); + param.setProperty("stateVar", stateVar_ProxyState); + param.setProperty("text", stateVar_ProxyState); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + return { + component: component, + name: "Child1", + }; +}), ((instance: ESValue) => {})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/interop/provide_consume_interop.ets b/arkui-plugins/test/demo/interop/provide_consume_interop.ets new file mode 100644 index 0000000000000000000000000000000000000000..48076c7918884869ca94f1c051360dcf7a448c9b --- /dev/null +++ b/arkui-plugins/test/demo/interop/provide_consume_interop.ets @@ -0,0 +1,106 @@ +/* + * Copyright (C) 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. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @Provide stateVar: MyText = new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1() + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Consume stateVar: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + } + } +} + + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + let setFindProvideInterop = global.getProperty("setFindProvideInterop"); + let findProvideInterop = ((providedPropName: string): Object => { + let provide = (this).findProvide(providedPropName); + if (((provide!?.getProxy()) === (undefined))) { + let setSource = ((value: Object) => { + provide!.set(value); + }); + let proxyState = createState.invoke(ESValue.wrap(provide!.get()), ESValue.wrap(setSource)); + let setProxy = ((value: Object) => { + proxyState.invokeMethod("set", ESValue.wrap(value)); + }); + let notifyCallback = ((propertyName: string) => { + proxyState.invokeMethod("notifyPropertyHasChangedPU"); + }); + configureState(provide!, proxyState, setProxy, notifyCallback); + } + let proxy = provide!.getProxy(); + return (proxy as ESValue).unwrap()!; + }); + setFindProvideInterop.invoke(findProvideInterop); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + structObject.invokeMethod("resetFindInterop") + return { + component: component, + name: "Child1", + }; + }), ((instance: ESValue) => {})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets b/arkui-plugins/test/demo/interop/state_link_interop.ets similarity index 96% rename from arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets rename to arkui-plugins/test/demo/interop/state_link_interop.ets index f11af005fc6f85e9e3a05e8f2f4cbd88a7f6f15c..ec3778ef278c48762a799525d4deaa9430717344 100644 --- a/arkui-plugins/test/demo/interop/1.2State-1.1Link-example.ets +++ b/arkui-plugins/test/demo/interop/state_link_interop.ets @@ -24,7 +24,7 @@ import { Child1 } from 'har1' @Component struct MyStateSample { - @State stateVar: new MyText(); + @State stateVar: MyText = new MyText(); build() { Column() { Button(this.stateVar.text) @@ -69,12 +69,12 @@ ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { let global = ESValue.getGlobal(); let param = ESValue.instantiateEmptyObject(); let createState = global.getProperty("createStateVariable"); - let stateVar_SetSource = ((value: B) => { + let stateVar_SetSource = ((value: MyText) => { (this).stateVar = value; }); let stateVar_ProxyState = createState.invoke(ESValue.wrap((this).stateVar), ESValue.wrap(stateVar_SetSource)); (this).__backing_stateVar!.setProxy(stateVar_ProxyState); - let stateVar_SetProxy = ((value: B) => { + let stateVar_SetProxy = ((value: MyText) => { stateVar_ProxyState.invokeMethod("set", ESValue.wrap(value)); }); (this).__backing_stateVar!.setProxyValue = stateVar_SetProxy; diff --git a/arkui-plugins/test/demo/interop/state_prop_interop.ets b/arkui-plugins/test/demo/interop/state_prop_interop.ets new file mode 100644 index 0000000000000000000000000000000000000000..99a073bbb2cd319bbabdd3d0bae4a9e97e4aa605 --- /dev/null +++ b/arkui-plugins/test/demo/interop/state_prop_interop.ets @@ -0,0 +1,93 @@ +/* + * Copyright (C) 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. + */ + + +// ArkTS1.2 +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { ArkUICompatible, InteropComponent, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { MyText } from 'har2/src/main/ets/components/MainPage' +import { Child1 } from 'har1' + + +@Component +struct MyStateSample { + @State stateVar: MyText = new MyText(); + build() { + Column() { + Button(this.stateVar.text) + .onClick((e: ClickEvent) => { + this.stateVar.text += '~'; + }) + Child1({stateVar: this.stateVar, text: this.stateVar}) + } + } +} + +class MyText { + text: string = 'MyText'; +} + + +//ArkT1.1 +import { MyText } from 'har2/src/main/ets/components/MainPage' + +@Component +export struct Child1{ + @Prop stateVar: MyText; + @Prop text: MyText; + build() { + Column() { + Button(this.stateVar.text) + .onClick(() => { + this.stateVar.text += '~'; + }) + Button(this.text.text) + .onClick(() => { + this.text.text = 'ArkTS1.1'; + }) + } + } +} + + +//transform 1.1struct 'Child1' to ArkUICompatible + +ArkUICompatible(__memo_context, ((__memo_id) + (252133223)), (() => { + let global = ESValue.getGlobal(); + let param = ESValue.instantiateEmptyObject(); + let createState = global.getProperty("createStateVariable"); + param.setProperty("stateVar", (this).stateVar); + param.setProperty("text", (this).stateVar); + let extraInfo = ESValue.instantiateEmptyObject(); + extraInfo.setProperty("page", "har1/src/main/ets/components/MainPage"); + let esundefined = ESValue.wrap(undefined); + let blank = (() => {}); + let esblank = ESValue.wrap((blank as object)); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let har1 = ESValue.load("@normalized:N&entry&com.example.Interop2use1&har1/src/main/ets/components/MainPage&1.0.0"); + let structObject = har1.getProperty("Child1"); + let component = structObject.instantiate(esundefined, param, esundefined, elmtId, esblank, extraInfo); + let create = structObject.getProperty("create"); + create.invoke(component); + return { + component: component, + name: "Child1", + }; +}), ((instance: ESValue) => { + instance.invokeMethod("updateStateVars", ESValue.wrap({stateVar: this.stateVar, text: this.stateVar})); +})); \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/build_config_template.json b/arkui-plugins/test/demo/localtest/build_config_template.json index 3855891d1837393be886fb41105f72b95307f0df..0d92ceecc8bba3706b7f2dcb6416e8452bde1476 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -1,25 +1,45 @@ { - "plugins": { - "ui-syntax-plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", - "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", - "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" - }, - - "compileFiles": [ - "./demo/localtest/entry/new.ets" - ], - - "packageName" : "entry", - - "buildType": "build", - "buildMode": "Debug", - "moduleRootPath": "./demo/localtest/entry/", - "sourceRoots": ["./"], - - "loaderOutPath": "./dist", - "cachePath": "./dist/cache", - - "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", - - "dependentModuleList": [] + "plugins": { + "ui_syntax_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", + "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", + "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" + }, + "compileFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "entryFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "buildMode": "Debug", + "projectRootPath": "./demo/localtest/", + "moduleRootPath": "./demo/localtest/entry/", + "cachePath": "./dist/cache", + "loaderOutPath": "./dist", + "compileSdkVersion": 20, + "compatibleSdkVersion": 20, + "bundleName": "com.example.myapplication", + "useNormalizedOHMUrl": true, + "buildType": "build", + "packageName": "entry", + "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", + "sourceRoots": [ + "./" + ], + "moduleType": "shared", + "moduleName": "entry", + "dependentModuleList": [], + "hasMainModule": true, + "buildLoaderJson": "", + "integratedHsp": false, + "allowEmptyBundleName": false, + "declgenV2OutPath": "", + "externalApiPaths": [], + "level": { + "level": 20000, + "levelStr": "INFO", + "colour": "green" + }, + "isBuildConfigModified": false, + "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5", + "es2pandaMode": 2 } \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/index.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets similarity index 100% rename from arkui-plugins/test/demo/localtest/entry/index.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets diff --git a/arkui-plugins/test/demo/localtest/entry/new.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets similarity index 36% rename from arkui-plugins/test/demo/localtest/entry/new.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets index acdb7c3c36ccd065e27692e8c22b6389c1b4e15c..76b8150735a73f4c837ce9da39f669ca01e07c3e 100755 --- a/arkui-plugins/test/demo/localtest/entry/new.ets +++ b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets @@ -13,60 +13,62 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins -import { Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins -import { State, Link, StateDecoratedVariable, LinkDecoratedVariable,MutableState, stateOf, observableProxy } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" import hilog from '@ohos.hilog' -function ArkUICompatible(init:(elmtId: number, instance: ESObject) => void, update: ((elmtId: number, instance: ESObject) => void)) { - -} - +@Entry @Component struct MyStateSample { - @State stateVar: string = "Parent"; - message: string = "var"; + @State stateVar: string = "state var"; + message: string = `click to change state variable, add **`; changeValue() { - this.stateVar+="~"; + this.stateVar+="**" } build() { - Column(undefined) { - Button("ParentChange").backgroundColor("#FFFF00FF") + Column() { + Button("clean variable").onClick((e: ClickEvent) => { this.stateVar = "state var" }) + Text("Hello World").fontSize(20) + Button(this.message).backgroundColor("#FFFF00FF") .onClick((e: ClickEvent) => { hilog.info(0x0000, 'testTag', 'On Click'); - this.changeValue(); + this.changeValue() }) Text(this.stateVar).fontSize(20) - ChildLink({stateVar: this.stateVar, stateVar1: this.stateVar, stateVar2: ""} as __Options_ChildLink) - } + Child({linkVar: this.stateVar, propVar: this.stateVar}) + }.margin(10) } } @Component -struct ChildLink { - @Link stateVar: string = "Child"; - @State stateVar1: string = "Child"; - @Link stateVar2: string = "Child"; - changeValue() { - this.stateVar+="~"; +struct Child { + @Link linkVar: string; + @Prop propVar: string = "Prop"; + + changeValue1() { + this.linkVar+="!!" } - build() { - Button("ChildChange").backgroundColor("#FFFF00FF") - .onClick((e: ClickEvent) => { - hilog.info(0x0000, 'testTag', 'On Click'); - this.changeValue(); - }) - Text(this.stateVar).fontSize(50) + + changeValue2() { + this.propVar+="~~" } -} -export class ComExampleTrivialApplication extends UserView { - getBuilder() { - hilog.info(0x0000, 'testTag', 'getBuilder'); - let wrapper = @memo () => { - hilog.info(0x0000, 'testTag', 'MyStateSample'); - MyStateSample(undefined); + build() { + Column() { + Button(`click to change Link variable, add symbol !!`) + .backgroundColor("#4169E1") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue1() + }) + Button(`click to change Prop variable, add symbol ~~`) + .backgroundColor("#3CB371") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue2() + }) + Text(`Link variable in child: ${this.linkVar}`).fontSize(30) + Text(`Prop variable in child: ${this.propVar}`).fontSize(30) } - return wrapper; } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f2ff2c7f12fb46a0656dd93857b72f7a1b057001 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 @@ -0,0 +1,37 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..bc9792df11412a3f3d08a60ca4a770f9a22ae3d5 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/index", + "pages/new" + ] +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets b/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..91cb598ca1c0c6b617ea9cdc17684ad20fea9506 --- /dev/null +++ b/arkui-plugins/test/demo/mock/animation/animatable-extend-basic.ets @@ -0,0 +1,32 @@ +/* + * Copyright (C) 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 { Text, Column, Component, Color, Curve } from "@ohos.arkui.component" +import { Entry } from "@ohos.arkui.component" + +@Entry +@Component +struct AnimatablePropertyExample { + build() { + Column() { + Text("AnimatableProperty") + .backgroundColor(Color.Red) + .animation({ duration: 2000, curve: Curve.Ease }) + .fontSize(20) + .animation({ duration: 2000, curve: Curve.Ease }) + .width("100%") + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets new file mode 100644 index 0000000000000000000000000000000000000000..77df2cf84f13616a2025eb300d99226cc0764665 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/block-in-switch-case.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchCase { + num: int = 2; + + build() { + Column() { + switch (this.num) { + case 0: + break; + case 1: + Text('111'); + case 2: + { + Text('111'); + } + case 3: + { + Text('111'); + } + break; + { + Text('111'); + } + case 4: + { + Text('111'); + break; + } + case 5: + { + Text('111'); + } + break; + case 6: + { + break; + } + case 7: + { + break; + Text('111'); + } + default: + break; + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..87e537547726b5ee4478ed95509aa20e93d09cd9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/else-if-in-content.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct ElseIf { + build() { + Column() { + if (true) { + } else if (false) { + Text('elseIf 1') + } else { + Text('else 1') + } + if (false) { + } else { + if (false) { + Text('elseIf 2') + } else { + Text('else 2') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..4f522cc1a49eb7fdd523d77bea16a1bdae07e692 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-break-in-nested-content.ets @@ -0,0 +1,26 @@ +import { Component, Column, Text } from "@ohos.arkui.component"; + +import hilog from "@ohos.hilog"; + +@Component +struct A { + build() { + Column() { + Column() { + Column() { + Column() { + if (false) { + hilog.info(0x0000, "very inside", "1"); + return; + Text("1"); + } + } + } + } + if (true) { + hilog.info(0x0000, "outside column", "2"); + Text("1"); + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..c9f98dbe9e06f13c5fb8937d7c27313812947cfe --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-else-in-content.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct IfElse { + build() { + Column() { + if (true) { + if (false) { + Text('if-if') + } else if (true) { + Text('if-elseIf') + } else { + Text('if-else') + } + } else if (false) { + Text('elseIf') + } else { + Text('else') + return; + Text('after-return') + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..51b00add02bbd0bd284eaa06edc4349e1cf71457 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/if-in-switch-in-content.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct IfInSwitch { + num: string = '1'; + + build() { + Column() { + switch (this.num) { + case '-1': { + if (true) { + Text('case 1') + } else if (false) { + Text('case 2') + } else { + Text('case 3') + } + } + case '2': + if (true) { + Text('case 4') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.units.d.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets similarity index 58% rename from arkui-plugins/test/local/@ohos.arkui.component.units.d.ets rename to arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets index c6a98e6281f1f588c75fa2dabba57a1d9c904c7e..2579f1ba9e259be8438bf050f30ae3071aabfa7f 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.units.d.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/non-builder-within-builder.ets @@ -13,17 +13,31 @@ * limitations under the License. */ -import { Resource } from "@ohos.arkui.external.resource" -import { Color } from "@ohos.arkui.component.enums"; +import { Component, Builder } from "@ohos.arkui.component" -export type PX = string; -export type VP = string | number; -export type FP = string; -export type LPX = string; -export type Percentage = string; +@Builder +function TestComponent( + init: TestInitCallback, + update: TestUpdateCallback, +): void {} -export type Dimension = PX | VP | FP | LPX | Percentage | Resource; +type TestInitCallback = () => void; +type TestUpdateCallback = () => void; -export type Length = string | number | Resource; +@Component +struct MyStateSample { + build() { + TestComponent( + () => { + if (true) { -export type ResourceColor = Color | number | string | Resource; \ No newline at end of file + } + }, + () => { + if (false) { + + } + } + ); + } +} diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..8944b5e0a5e05962e4410049c698a9f76a3cd698 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-case-in-content.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchCase { + num: string = '1'; + + build() { + Column() { + switch (this.num) { + case '0': + Text('case 0') + case '1': + Text('case 1') + break; + Text('after break') + case '2': + Text('case 2') + return; + Text('after return') + default: + Text('default') + } + Text('hello world') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets new file mode 100644 index 0000000000000000000000000000000000000000..2e2a0f047e2a248769397aace1ec313f7909566e --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/switch-in-if-in-content.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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 { Text, Column, Component } from "@ohos.arkui.component" + +@Component +struct SwitchInIf { + num: string = '1'; + + build() { + Column() { + if (true) { + switch (this.num) { + case '0': + Text('case 0') + } + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets new file mode 100644 index 0000000000000000000000000000000000000000..c2a8eaab837dcb47aafa6cf935d2971177b2e63a --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/condition-scope/with-builder.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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 { Text, Column, Component, Builder, BuilderParam, WrappedBuilder, wrapBuilder } from "@ohos.arkui.component" + +@Builder +function MyBuilder(): void { + if (true) { + Text('within Builder function'); + } +} + +@Builder +function ParamBuilder(@Builder param: (() => void) = () => { + if (true) { + Text('within Builder parameter') + } +}): void { + param(); +} + +const wBuilder = wrapBuilder(ParamBuilder); + +@Component +struct MyStruct { + @Builder + public myBuilderMethod() { + if (true) { + Text('within Builder method'); + } + } + + build() { + Column() { + wBuilder.builder(@Builder () => { + if (true) { + Text('with Builder lambda'); + } + }); + Child({ + myBuilderParam: () => { + if (true) { + Text('within Builder property') + } + this.myBuilderMethod(); + } + }) + } + } +} + +@Component +struct Child { + @BuilderParam myBuilderParam: () => void = () => { + if (true) { + Text('within BuilderParam property'); + } + } + build() { + if (true) { + Text('within struct build'); + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets index 4f0f3b5cf901bfd71f97ab91e53d2e33eee28531..79886cd636abb115db86135911840d922c0f2e79 100644 --- a/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets @@ -13,8 +13,8 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" -import { Column, UIColumnAttribute } from "arkui.component.column" +import { memo } from "@ohos.arkui.stateManagement" +import { Column, ColumnAttribute } from "arkui.component.column" class MyStateSample { @memo build() { diff --git a/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets b/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets index 92d842ff54db1ec685cfce72da7df394bef82ecc..a9b81be6801d733b9d0029d84f4298ce16ed41e8 100644 --- a/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.ets @@ -14,16 +14,16 @@ */ import { memo } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins -import { Text, UITextAttribute, Column, Component} from "@ohos.arkui.component" +import { Text, TextAttribute, Column, Component} from "@ohos.arkui.component" import hilog from '@ohos.hilog' -@memo function cardStyle(this: UITextAttribute, num: number, str: string): this { +@memo function cardStyle(this: TextAttribute, num: number, str: string): this { this.fontSize(num); this.backgroundColor(num); return this; } -@memo function style22(this: UITextAttribute): this { +@memo function style22(this: TextAttribute): this { this.fontWeight(700); return this; } diff --git a/arkui-plugins/test/demo/mock/common-utils/annotation.ets b/arkui-plugins/test/demo/mock/common-utils/annotation.ets new file mode 100644 index 0000000000000000000000000000000000000000..e8a78a587e9d3eb84e4e8de3d68bdef4238183c5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/common-utils/annotation.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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. + */ + +@Retention({policy:"SOURCE"}) @interface TestAnno {} + +type TestType = number; + +() => {}; + +class A { + prop: number = 1; + + method(arg1: number): void { + const a: number = arg1; + } +} + +interface __A { + prop: number; +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.d.ets b/arkui-plugins/test/demo/mock/component/declare-component.ets similarity index 64% rename from arkui-plugins/test/local/@ohos.arkui.component.d.ets rename to arkui-plugins/test/demo/mock/component/declare-component.ets index 0f74a9db4523b49fbb41c6f94bf1ca39b66f1fba..aa0142368b5354a8d4346902c8ce4f6f43cbdd6c 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.d.ets +++ b/arkui-plugins/test/demo/mock/component/declare-component.ets @@ -13,10 +13,14 @@ * limitations under the License. */ -export * from "@ohos.arkui.component.customComponent"; -export * from "@ohos.arkui.component.common"; -export * from "@ohos.arkui.component.column"; -export * from "@ohos.arkui.component.text"; -export * from "@ohos.arkui.component.styledString"; -export * from "@ohos.arkui.component.enums"; -export * from "@ohos.arkui.component.units"; \ No newline at end of file +import { Component, ResourceStr, Builder} from "@ohos.arkui.component" +import { Prop, State } from "@ohos.arkui.stateManagement" + +@Component +export declare struct SwipeRefresher { + @Prop content?: ResourceStr; + @Prop isLoading: boolean; + @State code: number; + + @Builder build(): void; +} diff --git a/arkui-plugins/test/demo/mock/component/for-each.ets b/arkui-plugins/test/demo/mock/component/for-each.ets new file mode 100644 index 0000000000000000000000000000000000000000..d615581b69ca73e9fb6383f887ede9e41e038b78 --- /dev/null +++ b/arkui-plugins/test/demo/mock/component/for-each.ets @@ -0,0 +1,55 @@ +/* + * Copyright (C) 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 { Component, Text, WrappedBuilder, Column, ForEach } from "@kit.ArkUI" + +interface Person { + name: string; + age: number +} + +class AB { + per: string = 'hello'; + bar: Array = new Array('xx', 'yy', 'zz') +} + +@Component +struct ImportStruct { + arr: string[] = ['a', 'b', 'c'] + getArray() { + return new Array({ name: 'LiHua', age:25 } as Person, { name: 'Amy', age:18 } as Person) + } + + build() { + Column() { + ForEach(this.arr, (item: string) => { + Text(item) + }) + ForEach(this.getArray(), (item: Person) => { + Text(item.name) + }) + ForEach((new AB()).bar, (item: string) => { + Text(item) + }) + ForEach((new AB()).bar, () => {}) + ForEach(this.getArray(), () => {}) + ForEach(new Array('1', '2'), () => { + ForEach(new Array('1', '2'), (item: string) => { + Text(item) + }) + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets b/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets new file mode 100644 index 0000000000000000000000000000000000000000..4bc1dca03081039f5abb327ba852cf8efb9085b9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/builder-param/optional-builder-param.ets @@ -0,0 +1,52 @@ +/* + * Copyright (C) 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 { Component, Entry, Builder, BuilderParam, Column, Text, Row } from "@kit.ArkUI" + +@Builder +function showTextBuilder() { + Text('Hello World') +} + +@Component +struct Child { + @BuilderParam customBuilderParam2?: () => void; + @BuilderParam customBuilderParam1: () => void = showTextBuilder; + + build() { + Row() { + if (this.customBuilderParam2) { + (this.customBuilderParam2 as () => void)() + } + if (this.customBuilderParam2) { + (this.customBuilderParam2!)() + } + this.customBuilderParam1() + } + } +} + +@Component +struct Parent { + @Builder componentBuilder() { + Text('Parent builder') + } + + build() { + Column() { + Child({ customBuilderParam2: () => { this.componentBuilder() } }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets new file mode 100644 index 0000000000000000000000000000000000000000..864244caa3e743f348dabf389489383fe0d08806 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-observedv2-class.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 { Computed, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Name { + @Trace firstName: string = 'Hua'; + @Trace lastName: string = 'Li'; + + @Computed + get fullName(): string { + console.info('---------Computed----------'); + return this.firstName + ' ' + this.lastName; + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets new file mode 100644 index 0000000000000000000000000000000000000000..ec221d404da24d6fde57e57ced996cb7bccd7851 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-in-struct.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, Button, Divider, Text } from "@ohos.arkui.component" +import { Computed, Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Local firstName: string = 'Li'; + @Local lastName: string = 'Hua'; + age: number = 20; + + @Computed + get fullName(): string { + console.info("---------Computed----------"); + return this.firstName + ' ' + this.lastName + this.age; + } + + build() { + Column() { + Text(this.lastName + ' ' + this.firstName) + Text(this.lastName + ' ' + this.firstName) + Divider() + Text(this.fullName) + Text(this.fullName) + Button('changed lastName').onClick((e) => { + this.lastName += 'a'; + }) + Button('changed age').onClick((e) => { + this.age++; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets b/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..c592a2300c0b095501c200a762dd9c6560e01045 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/computed-no-return-type.ets @@ -0,0 +1,55 @@ +/* + * Copyright (C) 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 { Computed, ObservedV2, Trace } from "@ohos.arkui.stateManagement" +import { ComponentV2, Column, Button, Divider, Text } from "@ohos.arkui.component" +import { Computed, Local } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Name { + @Trace firstName: string = 'Hua'; + @Trace lastName: string = 'Li'; + age: number = 20; + + @Computed + get fullName() { + if (this.age >= 20) { + return new Array(0, 1, 2); + } + return this.firstName + ' ' + this.lastName; + } +} + +@ComponentV2 +struct Index { + @Local firstName: string = 'Li'; + @Local lastName: string = 'Hua'; + age: number = 20; + + @Computed + get fullName() { + if (this.age >= 20) { + return 500 + } + return this.firstName + ' ' + this.lastName + this.age; + } + + @Computed + get num5() { + return 5; + } + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets b/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets new file mode 100644 index 0000000000000000000000000000000000000000..55efc5b71886fb52ab0bebcb596ad8be327525c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/computed/static-computed.ets @@ -0,0 +1,65 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component"; +import { Computed, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement"; + +@ObservedV2 +class Name { + @Trace static firstName: string = 'Hua'; + @Trace static lastName: string = 'Li'; + + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } +} + +@ComponentV2 +struct Parent { + @Local static localVar1: string = 'stateVar1'; + @Local static localVar2: number = 50; + + + @Computed + static get fullName(): string { + return Parent.localVar1; + } + + build() {} +} + +@ComponentV2 +struct Parent2 { + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } + + @Computed + static get fullName2(): string { + return Parent.localVar1; + } + + build() {} +} + +@ObservedV2 +class Name2 { + @Computed + static get fullName(): string { + return Name.firstName + ' ' + Name.lastName; + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets new file mode 100644 index 0000000000000000000000000000000000000000..e527defd13af18a3897f3b7d98dfe4aebe5f0af5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/base-custom-dialog.ets @@ -0,0 +1,98 @@ +/* + * Copyright (C) 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 { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" +import { + CustomDialog, + CustomDialogController, + DismissDialogAction, + DismissReason, + DialogAlignment, + CustomDialogControllerOptions +} from "@ohos.arkui.component" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string = 'text'; + cancel: () => void = () => { + } + confirm: () => void = () => { + } + @State hh: string = 'nihao' + build() { + Column() { + Text('CustomDialog One') + .fontSize(30) + .height(100) + Button('Close') + .onClick((e: ClickEvent) => { + if (this.aaController != undefined) { + this.aaController!.close(); + } + }) + .margin(20) + } + } +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample({ + cancel: ()=> { this.onCancel(); }, + confirm: ()=> { this.onAccept(); } + }), + cancel: this.existApp, + autoCancel: true, + alignment: DialogAlignment.Center, + offset: { dx: 0, dy: -20 }, + gridCount: 4, + showInSubWindow: true, + isModal: true, + customStyle: false, + cornerRadius: 10, + focusable: true + }) + + aboutToDisappear() { + this.dialogController = null; + } + + onCancel() { + console.info('Callback when the first button is clicked'); + } + + onAccept() { + console.info('Callback when the second button is clicked'); + } + + existApp() { + console.info('Click the callback in the blank area'); + } + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + if (this.dialogController != null) { + this.dialogController!.open(); + } + }).backgroundColor(0x317aff) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets new file mode 100644 index 0000000000000000000000000000000000000000..e041a012ed72630c43d5f60951e0b4ed8162671b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/builder-dialog-options.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 { Text, Column, Component, Builder } from "@ohos.arkui.component" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" +import hilog from '@ohos.hilog' + +@Builder function builder1(str: string) {} + +@Builder function builder2() {} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: () => { builder1('nihao') }, + }) + + build() { + Column() {} + } +} + +@Component +struct CustomDialogUser2 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: builder2, + }) + + build() { + Column() {} + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets new file mode 100644 index 0000000000000000000000000000000000000000..c87bc2e13d0018ae043a0da87d0c09b977ab2dba --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-build.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 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 { Text, Column, Component, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string = 'text'; + @State hh: string = 'nihao' + build() { + Column() { + Text('CustomDialog One') + } + } +} + +@Component +struct CustomDialogUser { + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + let dialogController: CustomDialogController | undefined = new CustomDialogController({ + builder: CustomDialogExample({}) + }) + }).backgroundColor(0x317aff) + } + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets new file mode 100644 index 0000000000000000000000000000000000000000..677b6298247ae00a1f46507529f406db63463837 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/controller-in-method.ets @@ -0,0 +1,47 @@ +/* + * Copyright (C) 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 { Component, CustomDialog, CustomDialogController } from "@ohos.arkui.component" +import hilog from '@ohos.hilog' + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + build() {} +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample({}), + }); + + updateController1() { + this.dialogController = new CustomDialogController({ + builder: CustomDialogExample({}), + autoCancel: true, + }); + } + + updateController2() { + let temp = new CustomDialogController({ + builder: CustomDialogExample({}), + autoCancel: true, + }); + this.dialogController = temp; + } + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets new file mode 100644 index 0000000000000000000000000000000000000000..72cc017a539e4492813d6a860ad6ad8ee381a1d9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/declare-custom-dialog.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 { State } from "@ohos.arkui.stateManagement" +import { CustomDialog, CustomDialogController, ComponentV2, Component, Builder } from "@ohos.arkui.component" + +@CustomDialog +export declare struct CustomDialogExample { + aaController?: CustomDialogController; + @State text: string; + hh: string + + @Builder build(): void; +} + +@Component +struct CustomDialogUserV1 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample(), + }) + + build() {} +} + +@ComponentV2 +struct CustomDialogUserV2 { + dialogController: CustomDialogController | null = new CustomDialogController({ + builder: CustomDialogExample(), + }) + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets b/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets new file mode 100644 index 0000000000000000000000000000000000000000..3829a2b74c8bb16add03dc09b2ddc66809dfa9b6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/custom-dialog/extends-dialog-controller.ets @@ -0,0 +1,64 @@ +/* + * Copyright (C) 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 { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { CustomDialog, CustomDialogController, CustomDialogControllerOptions } from "@kit.ArkUI" + +@CustomDialog +struct CustomDialogExample { + aaController?: CustomDialogController; + + build() { + Column() {} + } +} + +class DialogControllerV2 extends CustomDialogController { + options: CustomDialogControllerOptions; + + constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } +} + +class DialogControllerV3 extends DialogControllerV2 { + options: CustomDialogControllerOptions; + + constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } +} + +@Component +struct CustomDialogUser { + dialogController: CustomDialogController | null = new CustomDialogController({ + gridCount: 4, + showInSubWindow: true, + builder: CustomDialogExample(), + } as CustomDialogControllerOptions) as DialogControllerV3 + + build() { + Column() { + Button('click me') + .onClick((e: ClickEvent) => { + if (this.dialogController != null) { + this.dialogController!.open(); + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..65065c3e573871304305edf1856f30a5c953a00e --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets @@ -0,0 +1,98 @@ +/* + * Copyright (C) 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 { Component, ComponentV2, CustomDialog } from "@ohos.arkui.component" +import { State, Prop, Provide, Event, Local, Param } from "@ohos.arkui.stateManagement" +import { Provider, Consumer, Once, Observed, ObservedV2, Trace, Track } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ObservedV2 +class OO { + @Trace hi = '150' +} + +@Observed +class RR { + @Track hi = '150' +} + +@Component +struct Parent { + @State stateVar1 = new Per(6); + @Prop stateVar2 = new Array(3, 6, 8); + @Provide stateVar3 = StateType.TYPE3; + stateVar8 = (sr: string) => { + }; + stateVar9 = new Date('2025-4-23'); + @Provide({ alias: 'me0', allowOverride: false }) stateVar11113 = true; + @Provide stateVar11114 = undefined; + @State stateVar11115 = null; + + build() { + } +} + +@ComponentV2 +struct V2Parent { + @Once @Param stateVar4 = new Set(new Array('aa', 'bb')); + @Local stateVar5 = [true, false]; + @Local stateVar6 = new Array(new Per(7), new Per(11)); + stateVar7 = [new Per(7), new Per(11)]; + @Event stateVar8 = (sr: string) => { + }; + @Param stateVar9 = new Date('2025-4-23'); + @Param stateVar10 = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param stateVar11 = 0.0; + @Provider stateVar12 = new Per(6); + @Consumer stateVar11111 = 'stateVar1'; + @Provider('var') stateVar11188 = ''; + @Consumer('nihao') stateVar11112 = 50; + + build() { + } +} + +@CustomDialog +struct CC { + @Once @Param stateVar4 = new Set(new Array('aa', 'bb')); + @Local stateVar5 = [true, false]; + @Local stateVar6 = new Array(new Per(7), new Per(11)); + stateVar7 = [new Per(7), new Per(11)]; + @Event stateVar8 = (sr: string) => { + }; + @Param stateVar9 = new Date('2025-4-23'); + @Param stateVar10 = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param stateVar11 = 0.0; + @Provider stateVar12 = new Per(6); + @Consumer stateVar11111 = 'stateVar1'; + @Provider('var') stateVar11188 = ''; + @Consumer('nihao') stateVar11112 = 50; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets b/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets new file mode 100644 index 0000000000000000000000000000000000000000..a596e9324b48f3d19ef390534be812e437e6a7a4 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/event/event-initialize.ets @@ -0,0 +1,52 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, Text } from "@ohos.arkui.component" +import { Event, Param, Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Child { + @Param index: number = 0; + @Event changeIndex: (val: number) => void; + @Event testEvent: (val: number) => number; + @Event testEvent2: (val: number) => number = (val: number) => { return val }; + + build() { + Column() { + Text(`Child index: ${this.index}`) + .onClick((e) => { + this.changeIndex(20); + console.log(`after changeIndex ${this.index}`); + }) + } + } +} + +@ComponentV2 +struct Index { + @Local index: number = 0; + + build() { + Column() { + Child({ + index: this.index, + changeIndex: (val: number) => { + this.index = val; + console.log(`in changeIndex ${this.index}`); + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..178326815e36b14b66da2478a372ba3522282597 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/local-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Local } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Local localVar1: string = 'stateVar1'; + @Local localVar2: number = 50; + @Local localVar3: boolean = true; + @Local localVar4: undefined = undefined; + @Local localVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..7a0d9f40ededb353f3ae58137c0ba9ee059dcae4 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/local-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Local } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Local localVar1: Per = new Per(6); + @Local localVar2: Array = new Array(3,6,8); + @Local localVar3: StateType = StateType.TYPE3; + @Local localVar4: Set = new Set(new Array('aa', 'bb')); + @Local localVar5: boolean[] = [true, false]; + @Local localVar6: Array = new Array(new Per(7), new Per(11)); + @Local localVar7: Per[] = [new Per(7), new Per(11)]; + @Local localVar9: Date = new Date('2025-4-23'); + @Local localVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Local localVar11: string | number = 0.0; + @Local localVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/local/static-local.ets b/arkui-plugins/test/demo/mock/decorators/local/static-local.ets new file mode 100644 index 0000000000000000000000000000000000000000..4d84fcbc2f1c9dd6ddcc2a3ac132711888cdb2c1 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/local/static-local.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component"; +import { Local } from "@ohos.arkui.stateManagement"; + +class ABB {} + +@ComponentV2 +struct Parent { + @Local static localVar1: string = 'stateVar1'; + @Local static localVar2: number = 50; + @Local static localVar3: ABB = new ABB(); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..4a1099b23f4a7f05e2293c4b53baa2ddac583ac1 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type.ets @@ -0,0 +1,43 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorageLink } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Entry +@Component +struct MyStateSample { + @LocalStorageLink('Prop1') arrayA: number[] = [1,2,3]; + @LocalStorageLink('Prop2') objectA: Object = {}; + @LocalStorageLink('Prop3') dateA: Date = new Date('2021-08-08'); + @LocalStorageLink('Prop4') setA: Set = new Set(); + @LocalStorageLink('Prop5') mapA: Map = new Map(); + //@LocalStorageLink('Prop6') unionA: string | undefined = ""; + @LocalStorageLink('Prop7') classA: Person = new Person("John"); + @LocalStorageLink('Prop8') enumA: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..8bc882e51e4005925ef19ce4f0e73539725c79cf --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type.ets @@ -0,0 +1,27 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorageLink } from "@ohos.arkui.stateManagement" + +@Entry +@Component +struct MyStateSample { + @LocalStorageLink('Prop1') numA: number = 33; + @LocalStorageLink('Prop2') stringA: string = 'AA'; + @LocalStorageLink('Prop3') booleanA: boolean = true; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..72cc3f9c772a57ad4d743dd8a2a1a96116257569 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.ets @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { LocalStoragePropRef } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Component +struct MyStateSample { + @LocalStoragePropRef('Prop1') arrayB: number[] = [1,2,3]; + @LocalStoragePropRef('Prop2') objectB: Object = {}; + @LocalStoragePropRef('Prop3') dateB: Date = new Date('2021-09-09'); + @LocalStoragePropRef('Prop4') setB: Set = new Set(); + @LocalStoragePropRef('Prop5') mapB: Map = new Map(); + @LocalStoragePropRef('Prop7') classB: Person = new Person("Kevin"); + @LocalStoragePropRef('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..dce193d2fc340f85be92d3f789e7feb2c4d61649 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { LocalStoragePropRef } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + @LocalStoragePropRef('Prop1') numB: number = 43; + @LocalStoragePropRef('Prop2') stringB: string = 'BB'; + @LocalStoragePropRef('Prop3') booleanB: boolean = false; + @LocalStoragePropRef('Prop4') undefinedB: undefined = undefined; + @LocalStoragePropRef('Prop5') nullB: null = null; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets b/arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets similarity index 47% rename from arkui-plugins/test/local/@ohos.arkui.component.column.d.ets rename to arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets index 915dc5cebb40010bb552e9afc7e12b6b74ff64b6..bef93f0e0e567b08bd2f1e175cd4a2079913c63a 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/monitor/enum-monitor-params.ets @@ -13,29 +13,37 @@ * limitations under the License. */ -import { memo } from '../stateManagement/runtime'; -import { ComponentBuilder, CommonMethod, PointLightStyle } from './common'; -import { HorizontalAlign, FlexAlign } from './enums'; +import { ComponentV2 } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement" -export declare interface ColumnOptions { - space?: string | number; +class FFF { + ff: GGG = new GGG() } -export declare interface ColumnAttribute extends CommonMethod { - @memo - alignItems(value: HorizontalAlign): this; - @memo - justifyContent(value: FlexAlign): this; - @memo - pointLight(value: PointLightStyle): this; - @memo - reverse(isReversed?: boolean): this; +class GGG {} + +enum MonitorNames { + name1 = "strArr.0", + name2 = "name", + name3 = MonitorNames.name1, + name4 = "varF.ff", +} + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace strArr: string[] = ["North", "east"]; + + @Monitor([MonitorNames.name1, MonitorNames.name2, MonitorNames.name3]) + changeCCC(monitor: IMonitor) {} } -@memo -@ComponentBuilder -export declare function Column ( - options?: ColumnOptions, - @memo - content?: () => void -): ColumnAttribute; \ No newline at end of file +@ComponentV2 +struct Index { + @Local varF: FFF = new FFF() + + @Monitor([MonitorNames.name4]) + changeEEE(monitor: IMonitor) {} + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a0e4d4c6da54e1d8de6a64319187bf921605d7b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-before-state-variable.ets @@ -0,0 +1,45 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Monitor, Local, IMonitor } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Monitor(["message", "name"]) + onStrChange1(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Monitor(["message", "name"]) + onStrChange2(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Monitor(["name"]) + onStrChange3(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + @Local message: string = "Hello World"; + @Local name: string = "Tom"; + @Local age: number = 24; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets new file mode 100644 index 0000000000000000000000000000000000000000..a10548d25c6805cc35facd9822e1de3bdaa1eaec --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-observedv2-class.ets @@ -0,0 +1,67 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace region: string = "North"; + @Trace job: string = "Teacher"; + age: number = 25; + + @Monitor(["name"]) + onNameChange(monitor: IMonitor) { + console.info(`name change from ${monitor.value()?.before} to ${monitor.value()?.now}`); + } + + @Monitor(["age"]) + onAgeChange(monitor: IMonitor) { + console.info(`age change from ${monitor.value()?.before} to ${monitor.value()?.now}`); + } + + @Monitor(["region", "job"]) + onChange(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} change from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }) + } +} + +@ComponentV2 +struct Index { + info: Info = new Info(); + build() { + Column() { + Button("change name") + .onClick((e) => { + this.info.name = "Jack"; + }) + Button("change age") + .onClick((e) => { + this.info.age = 26; + }) + Button("change region") + .onClick((e) => { + this.info.region = "South"; + }) + Button("change job") + .onClick((e) => { + this.info.job = "Driver"; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets new file mode 100644 index 0000000000000000000000000000000000000000..412af0371e5485ce0b690f27dd37258092b24825 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-in-struct.ets @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, Local, IMonitor } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Index { + @Local message: string = "Hello World"; + @Local name: string = "Tom"; + @Local age: number = 24; + @Monitor(["message", "name"]) + onStrChange(monitor: IMonitor) { + monitor.dirty.forEach((path: string) => { + console.info(`${path} changed from ${monitor.value(path)?.before} to ${monitor.value(path)?.now}`); + }); + } + + build() { + Column() { + Button("change string") + .onClick((e) => { + this.message += "!"; + this.name = "Jack"; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets similarity index 34% rename from arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets rename to arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets index 728c949e75b926d8782cb3bc3fbb3b05ca67d079..395c70a69a07eece897d0f24e03215bca44c864f 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/monitor/monitor-params.ets @@ -13,45 +13,66 @@ * limitations under the License. */ -import { memo } from "@ohos.arkui.stateManagement.runtime"; -import { ComponentBuilder, CommonMethod } from "@ohos.arkui.component.common"; -import { Length, ResourceColor } from "@ohos.arkui.component.units"; - -@Retention({policy: "SOURCE"}) -export declare @interface Component {}; - -@Retention({policy: "SOURCE"}) -export declare @interface Entry { routeName: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Reusable {}; - -export declare abstract class CustomComponent, T_Options> implements - CommonMethod { - - @memo - @ComponentBuilder - static $_instantiate, S_Options>( - factory: () => S, - initializers?: S_Options, - @memo - content?: () => void - ): S; - - // Life cycle for custom component - aboutToAppear(): void; - aboutToDisappear(): void; - aboutToReuse(): void; - aboutToRecycle(): void; - - @memo - build(): void; - - // Implementation of common method - @memo - width(w: Length): this; - @memo - height(h: Length): this; - @memo - backgroundColor(color: ResourceColor): this; +import { ComponentV2, Column, Button } from "@ohos.arkui.component" +import { Monitor, IMonitor, ObservedV2, Trace, Local } from "@ohos.arkui.stateManagement" + +class AAA { + aaa: BBB = new BBB() +} + +class BBB { + bbb: CCC = new CCC() +} + +class CCC { + ccc: Array = new Array(new DDD(10), new DDD(12), new DDD(16)) +} + +class DDD { + dd: number[] + constructor(dd: number) { + this.dd = []; + for(let i = 4; i < dd; i++) { + this.dd.push(i); + } + } +} + +class EEE { + ee: FFF = new FFF() +} + +class FFF { + ff: GGG = new GGG() +} + +class GGG {} + +@ObservedV2 +class Info { + @Trace name: string = "Tom"; + @Trace strArr: string[] = ["North", "east"]; + @Trace job: AAA = new AAA(); + age: number = 25; + + @Monitor(["name"]) + onNameChange(monitor: IMonitor) {} + + @Monitor(["strArr.0", "age"]) + onAgeChange(monitor: IMonitor) {} + + @Monitor(["job.aaa.bbb.ccc.1.dd.0"]) + onJobChange(monitor: IMonitor) {} +} + +@ComponentV2 +struct Index { + @Local per: EEE = new EEE() + @Local v1: boolean = true + @Local numArr: string[] = ['1','3','5'] + + @Monitor(["per.ee.ff", "v1", "numArr.1"]) + onPerChange(monitor: IMonitor) {} + + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets index 273a00f461eee3188e82f24de278f0e8e7873778..b13a744360194a5d7938cc9ef2dbf24b4ac95688 100644 --- a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets @@ -1,13 +1,39 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed, ObjectLink } from "@ohos.arkui.stateManagement" @Observed class A {} +@Observed +class B {} + @Component struct MyStateSample { - @ObjectLink objectlinkvar: A; + build() {} +} + +@Component +struct MyStateSample2 { + @ObjectLink objectlinkvar1: A | undefined; + @ObjectLink objectlinkvar2: A | B; + @ObjectLink objectlinkvar3: A | B | null; + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets index 54dc8f89b6cf6e4dd5a660e4fe18b4f16835e154..73cc53cc0b59037dac3b14f09d8eb605e066520a 100644 --- a/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry, Column, Button, ClickEvent } from "@ohos.arkui.component" import { State, ObjectLink, Observed } from "@ohos.arkui.stateManagement" @@ -10,7 +25,7 @@ class DateClass extends Date { @Observed class NewDate { - public data: DateClass; + public data: DateClass = new DateClass(11); constructor(data: DateClass) { this.data = data; @@ -42,11 +57,11 @@ struct Parent { Child({ label: 'date', data: this.newData.data }) Button(`parent update the new date`) - .onClick(() => { + .onClick((e: ClickEvent) => { this.newData.data = new DateClass('2023-07-07'); }) Button(`ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))`) - .onClick(() => { + .onClick((e: ClickEvent) => { this.newData = new NewDate(new DateClass('2023-08-20')); }) } diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets new file mode 100644 index 0000000000000000000000000000000000000000..a9e0cab8c58ba61b921655638a7026ed035b6625 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonrename.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@Observed +class testJsonRename { + var1: number = 1 + @Track var2: number = 2 + @JSONRename('name3') var3: number = 3 + @TestDecor var4: number = 4 + @JSONRename('name5') @Track var5: number = 5 + @JSONRename('name6') @TestDecor var6: number = 6 + @TestDecor @Track var7: number = 7 + @JSONRename('name8') @TestDecor @Track var8: number = 8 +} + +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets new file mode 100644 index 0000000000000000000000000000000000000000..725a7078ea034e36ccbbdb0174a337475cfa9545 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-jsonstringifyignore.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@Observed +class testJSONStringifyIgnore { + var1: number = 1 + @Track var2: number = 2 + @JSONStringifyIgnore var3: number = 3 + @TestDecor var4: number = 4 + @JSONStringifyIgnore @Track var5: number = 5 + @JSONStringifyIgnore @TestDecor var6: number = 6 + @TestDecor @Track var7: number = 7 + @JSONStringifyIgnore @TestDecor @Track var8: number = 8 +} + +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets index 3890ea9ecab4eab9d52cbc7ab3c671cbd9c99a01..b646f91c4207d279a79d501ba4c9dddc04c50c72 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets index 48a3cd405f001f8c617ea967feef02215bb56963..10a9ecd577221c482ab69a668951ffef56883133 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets index bf59d6d517a1b9afd425c9abed0bf66974e54bb6..a47a08b816fe9c2c221f05c75965a8ba32c1e00b 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets index 66135926a24c14231dbfb442f8e3e3a2db1c8ce2..a9d102691bbf11ef993e7fb9682bdebc3adb3221 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets index 5be8bd871c70edeba919e09727f2bd6428ff040b..8342cd8c277e619aa2582af34fe7d9195b53d859 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed } from "@ohos.arkui.stateManagement" interface PropInterface { diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets index 3d15c071a043cde1cd20ab328133e032d0b9780a..b4023bb2e7fa862f71f8ed2a60d958bd5a9fe9bf 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Observed, Track } from "@ohos.arkui.stateManagement" @@ -5,6 +20,11 @@ import { Observed, Track } from "@ohos.arkui.stateManagement" class B { propB: number = 1 @Track trackB: number = 2 + @Track newProp: boolean; + + constructor(newProp: boolean) { + this.newProp = newProp; + } } @Component diff --git a/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets b/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets index fea1df86e1bfd14eeb87a1c65eb24129337a935a..08239c86f16a9ca62d911f6188714cb23cdf4e24 100644 --- a/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" import { Track } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets new file mode 100644 index 0000000000000000000000000000000000000000..89a06a3cb68deead0011586e018d95ad550e79db --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/base-observedv2-trace.ets @@ -0,0 +1,33 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class CC { + propB: number = 1 + @Trace traceB: number = 2 +} + +@ObservedV2 +class DD { + propB: number = 1 + @Trace traceE: number = 2 + @Trace tracef: number = 2 + vv: string + constructor(vv1: string) { + this.vv = vv1 + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets new file mode 100644 index 0000000000000000000000000000000000000000..035c520612aef06b76325a2ead9378cce477c6f3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-serialization.ets @@ -0,0 +1,43 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@Retention({policy: "SOURCE"}) +export declare @interface TestDecor {} + +@ObservedV2 +class testJSONStringifyIgnore { + var1: number = 1 + @Trace var2: number = 2 + @JSONStringifyIgnore var3: number = 3 + @TestDecor var4: number = 4 + @JSONStringifyIgnore @Trace var5: number = 5 + @JSONStringifyIgnore @TestDecor var6: number = 6 + @TestDecor @Trace var7: number = 7 + @JSONStringifyIgnore @TestDecor @Trace var8: number = 8 +} + +@ObservedV2 +class testJsonRename { + var1: number = 1 + @Trace var2: number = 2 + @JSONRename('name3') var3: number = 3 + @TestDecor var4: number = 4 + @JSONRename('name5') @Trace var5: number = 5 + @JSONRename('name6') @TestDecor var6: number = 6 + @TestDecor @Trace var7: number = 7 + @JSONRename('name8') @TestDecor @Trace var8: number = 8 +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets new file mode 100644 index 0000000000000000000000000000000000000000..94c66ff97c3d432b9639368884a12ae72ca0d7c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-extends.ets @@ -0,0 +1,32 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class A { + propA: number = 1 + @Trace traceA: number = 2 +} + + +class G extends A { + propG: number = 1; +} + +@ObservedV2 +class H extends G { + @Trace propG: number = 1; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets new file mode 100644 index 0000000000000000000000000000000000000000..fdd79e4e5420e52c33bda12058a0ee9f3750c5b8 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-implements.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +interface PropInterface { + propF: number +} + +interface trackInterface { + trackF: number +} + +@ObservedV2 +class F implements PropInterface, trackInterface { + @Trace propF: number = 1 + trackF: number = 2 + @Trace bb: boolean + constructor(bb: boolean) { + this.bb = bb + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets new file mode 100644 index 0000000000000000000000000000000000000000..a5197ef85fc7533691e77c2fc8ca126551f484a4 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/observedv2-trace-types.ets @@ -0,0 +1,51 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +class Person{} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@ObservedV2 +class mixed1 { + @Trace numA: number = 33; + @Trace stringA: string = 'AA'; + @Trace booleanA: boolean = true; + @Trace arrayA: number[] = [1,2,3]; + @Trace objectA: Object = {}; + @Trace dateA: Date = new Date('2021-08-08'); + @Trace setA: Set = new Set(); + @Trace mapA: Map = new Map(); + @Trace unionA: string | undefined = ""; + @Trace classA: Person = new Person(); + @Trace enumA: Status = Status.NotFound; + + numB: number = 33; + stringB: string = 'AA'; + booleanB: boolean = true; + arrayB: number[] = [1,2,3]; + objectB: Object = {}; + dateB: Date = new Date('2021-08-08'); + setB: Set = new Set(); + mapB: Map = new Map(); + unionB: string | undefined = ""; + classB: Person = new Person(); + enumB: Status = Status.NotFound; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets new file mode 100644 index 0000000000000000000000000000000000000000..ef145e65f0aaa19d6e45dcccfdc74762e6c205a5 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/static-observedv2-trace.ets @@ -0,0 +1,23 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class CC { + propB: number = 1 + static propA: number = 1 + @Trace static traceB: number = 2 +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets new file mode 100644 index 0000000000000000000000000000000000000000..5210335ab8e9c36c8998009c72015104259554a0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observedv2-trace/trace-with-constructor.ets @@ -0,0 +1,26 @@ +/* + * Copyright (C) 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 { ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class FF { + propB: number = 1 + @Trace traceE: number = 2 + @Trace vv: string + constructor(vv1: string) { + this.vv = vv1 + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..dc79a560473e01bc4b53dd1ec54af303c2ff4649 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Param, Once } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Param @Once onceVar1: string = 'stateVar1'; + @Param @Once onceVar2: number = 50; + @Param @Once onceVar3: boolean = true; + @Param @Once onceVar4: undefined = undefined; + @Param @Once onceVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..0ce4ad2402cf2cf876071988c90393c24883db08 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Param, Once } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Once @Param onceVar1: Per = new Per(6); + @Once @Param onceVar2: Array = new Array(3,6,8); + @Once @Param onceVar3: StateType = StateType.TYPE3; + @Once @Param onceVar4: Set = new Set(new Array('aa', 'bb')); + @Once @Param onceVar5: boolean[] = [true, false]; + @Once @Param onceVar6: Array = new Array(new Per(7), new Per(11)); + @Once @Param onceVar7: Per[] = [new Per(7), new Per(11)]; + @Once @Param onceVar8: (sr: string)=>void = (sr: string)=>{}; + @Once @Param onceVar9: Date = new Date('2025-4-23'); + @Once @Param onceVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Once @Param onceVar11: string | number = 0.0; + @Once @Param onceVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-only.ets b/arkui-plugins/test/demo/mock/decorators/once/once-only.ets new file mode 100644 index 0000000000000000000000000000000000000000..ba4f2892a3073c55d0f35594b46651fa82a5c7e9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-only.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Once } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Child { + @Once onceParamNum: number = 0; + @Once onceVar4: Set = new Set(new Array('aa', 'bb')); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets b/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets new file mode 100644 index 0000000000000000000000000000000000000000..e346a73dabf5f08c2dc9ff3585aafeeeb8c49e56 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/once/once-with-require.ets @@ -0,0 +1,56 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, Text, Button } from "@ohos.arkui.component" +import { Param, Once, ObservedV2, Trace, Require, Local } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class Info { + @Trace name: string = "info"; +} + +@ComponentV2 +struct Child { + @Param @Once onceParamNum: number = 0; + @Param @Once @Require onceParamInfo: Info; + + build() { + Column() { + Text(`Child onceParamNum: ${this.onceParamNum}`) + Text(`Child onceParamInfo: ${this.onceParamInfo.name}`) + Button("changeOnceParamNum") + .onClick((e) => { + this.onceParamNum++; + }) + } + } +} + +@ComponentV2 +struct Index { + @Local localNum: number = 10; + @Local localInfo: Info = new Info(); + + build() { + Column() { + Text(`Parent localNum: ${this.localNum}`) + Text(`Parent localInfo: ${this.localInfo.name}`) + Child({ + onceParamNum: this.localNum, + onceParamInfo: this.localInfo + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..a80f8d1d3d855cd055406744c2d0df69e7838d65 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Param } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Param paramVar1: string = 'stateVar1'; + @Param paramVar2: number = 50; + @Param paramVar3: boolean = true; + @Param paramVar4: undefined = undefined; + @Param paramVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..52a543a33cb7d50115364d2ba7c35008272a1871 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-complex-type.ets @@ -0,0 +1,48 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Param } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Param paramVar1: Per = new Per(6); + @Param paramVar2: Array = new Array(3,6,8); + @Param paramVar3: StateType = StateType.TYPE3; + @Param paramVar4: Set = new Set(new Array('aa', 'bb')); + @Param paramVar5: boolean[] = [true, false]; + @Param paramVar6: Array = new Array(new Per(7), new Per(11)); + @Param paramVar7: Per[] = [new Per(7), new Per(11)]; + @Param paramVar8: (sr: string)=>void = (sr: string)=>{}; + @Param paramVar9: Date = new Date('2025-4-23'); + @Param paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Param paramVar11: string | number = 0.0; + @Param paramVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets b/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets new file mode 100644 index 0000000000000000000000000000000000000000..b6e542ad2c32debc419b468f96dbadcdd8ef66c9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/param/param-with-require.ets @@ -0,0 +1,75 @@ +/* + * Copyright (C) 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 { ComponentV2, Column, ForEach, Button, Text } from "@ohos.arkui.component" +import { Param, Require, Local } from "@ohos.arkui.stateManagement" + +class Region { + x: number; + y: number; + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} + +class Info { + name: string; + age: number; + region: Region; + constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } +} + +@ComponentV2 +struct Index { + @Local infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; + build() { + Column() { + ForEach(this.infoList, (info: Info) => { + MiddleComponent({ info: info }) + }) + Button("change") + .onClick((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + }) + } + } +} +@ComponentV2 +struct MiddleComponent { + @Require @Param info: Info; + build() { + Column() { + Text(`name: ${this.info.name}`) + Text(`age: ${this.info.age}`) + SubComponent({ region: this.info.region }) + } + } +} +@ComponentV2 +struct SubComponent { + @Require @Param region: Region; + build() { + Column() { + Text(`region: ${this.region.x}-${this.region.y}`) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..917703d1e7d0ecdefaf85813871e1298d26371ed --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @PropRef propVar1: string = 'propVar1'; + @PropRef propVar2: number = 50; + @PropRef propVar3: boolean = true; + @PropRef propVar4: undefined = undefined; + @PropRef propVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..419b95b45b23067cc1aa5970a72c092697e3f076 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-complex-type.ets @@ -0,0 +1,49 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @PropRef propVar1: Per = new Per(6); + @PropRef propVar2: Array = new Array(3,6,8); + @PropRef propVar3: PropType = PropType.TYPE3; + @PropRef propVar4: Set = new Set(new Array('aa', 'bb')); + @PropRef propVar5: boolean[] = [true, false]; + @PropRef propVar6: Array = new Array(new Per(7), new Per(11)); + @PropRef propVar7: Per[] = [new Per(7), new Per(11)]; + @PropRef propVar8: (sr: string)=>void = (sr: string)=>{}; + @PropRef propVar9: Date = new Date('2025-4-23'); + @PropRef propVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @PropRef propVar11: string | number = 0.0; + @PropRef propVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets new file mode 100644 index 0000000000000000000000000000000000000000..66002c9f04c95d8384f13b75c15c67d773beb534 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/prop-ref-without-initialization.ets @@ -0,0 +1,30 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { PropRef } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @PropRef propVar1: string; + @PropRef propVar2?: number; + @PropRef propVar3: boolean; + @PropRef propVar4: undefined; + @PropRef propVar5: null; + @PropRef propVar6: Array | null; + @PropRef propVar7: Map | undefined; + + build() { } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets b/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets new file mode 100644 index 0000000000000000000000000000000000000000..e729a9c7c7c4dc5b77c229091737ef30112a94fb --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop-ref/state-to-prop-ref.ets @@ -0,0 +1,54 @@ +/* + * Copyright (C) 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 { Component, Text, Button, Column, ClickEvent } from "@ohos.arkui.component" +import { PropRef, State } from "@ohos.arkui.stateManagement" + +@Component +struct CountDownComponent { + @PropRef count: number = 0; + costOfOneAttempt: number = 1; + + build() { + Column() { + if (this.count > 0) { + Text('You have'+ this.count + 'Nuggets left') + } else { + Text('Game over!') + } + Button('Try again').onClick((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + }) + } + } +} + +@Component +struct ParentComponent { + @State countDownStartValue: number = 10; + + build() { + Column() { + Text('Grant' + this.countDownStartValue + 'nuggets to play.') + Button('+1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue += 1; + }) + Button('-1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue -= 1; + }) + CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..bcc75a5d61b6254659b266e12d4b69af4aff8c68 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { Consume } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @Consume conVar1: string; + @Consume conVar2: number; + @Consume conVar3: boolean; + @Consume conVar4: undefined; + @Consume conVar5: null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..848f1dbf470951722c7fb7479c514877ba944e28 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/consume-complex-type.ets @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" +import { Consume } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @Consume conVar1: Per; + @Consume conVar2: Array; + @Consume conVar3: PropType; + @Consume conVar4: Set; + @Consume conVar5: boolean[]; + @Consume conVar6: Array; + @Consume conVar7: Per[]; + @Consume conVar8: (sr: string)=>void; + @Consume conVar9: Date; + @Consume conVar10: Map; + @Consume conVar11: string | number; + @Consume conVar12: Set | Per; + @Consume conVar13: Set | null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets new file mode 100644 index 0000000000000000000000000000000000000000..c0768056a465271652e3323a7317759d7f64f103 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-to-consume.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 { Component, Column, Text } from "@ohos.arkui.component" +import { Consume, Provide } from "@ohos.arkui.stateManagement" + +@Component +struct Child { + @Consume num: number; + @Consume('ss') str: string; + + build() { + Column() { + Text(`Child num: ${this.num}`) + Text(`Child str: ${this.str}`) + } + } +} + +@Component +struct Parent { + @Provide num: number = 10; + @Provide({ alias: 'ss' }) str: string = 'hello'; + + build() { + Column() { + Text(`Parent num: ${this.num}`) + Text(`Parent str: ${this.str}`) + Child() + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..cedc4f2eb37fb4ab54838328b81e482ca3c3cdfe --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Consumer } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Consumer consumerVar1: string = 'propVar1'; + @Consumer consumerVar2: number = 50; + @Consumer consumerVar3: boolean = true; + @Consumer consumerVar4: undefined = undefined; + @Consumer consumerVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.common.d.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets similarity index 38% rename from arkui-plugins/test/local/@ohos.arkui.component.common.d.ets rename to arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets index 2ff6ea60ae0b042792ac8796396371e815a65a0e..243a52feba2cf9c8bf77761bcfb2d418b70054de 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.common.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/consumer-complex-type.ets @@ -13,43 +13,35 @@ * limitations under the License. */ -import { Dimension, Length, ResourceColor } from "@ohos.arkui.component.units"; -import { IlluminatedType } from "@ohos.arkui.component.enums"; -import { memo } from '@ohos.arkui.stateManagement.runtime'; - -@Retention({policy: "SOURCE"}) -export @interface BuilderLambda { - value: string -} - -@Retention({policy: "SOURCE"}) -export declare @interface ComponentBuilder {}; - -@Retention({policy: "SOURCE"}) -export declare @interface BuilderParam {}; - -@Retention({policy: "SOURCE"}) -export declare @interface Builder {}; - -export declare interface LightSource { - positionX: Dimension; - positionY: Dimension; - positionZ: Dimension; - intensity: number; - color?: ResourceColor; +import { ComponentV2 } from "@ohos.arkui.component" +import { Consumer } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } } -export declare interface PointLightStyle { - lightSource?: LightSource; - illuminated?: IlluminatedType; - bloom?: number; +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 } -export declare interface CommonMethod { - @memo - width(w: Length): this; - @memo - height(h: Length): this; - @memo - backgroundColor(color: ResourceColor): this; +@ComponentV2 +struct Parent { + @Consumer paramVar1: Per = new Per(6); + @Consumer paramVar2: Array = new Array(3,6,8); + @Consumer paramVar3: StateType = StateType.TYPE3; + @Consumer paramVar4: Set = new Set(new Array('aa', 'bb')); + @Consumer paramVar5: boolean[] = [true, false]; + @Consumer paramVar6: Array = new Array(new Per(7), new Per(11)); + @Consumer paramVar7: Per[] = [new Per(7), new Per(11)]; + @Consumer paramVar9: Date = new Date('2025-4-23'); + @Consumer paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Consumer paramVar11: string | number = 0.0; + @Consumer paramVar12: Set | Per = new Per(6); + + build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..312eed3373e1d928864ec550d582b37ba9ce7fb1 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Provider } from "@ohos.arkui.stateManagement" + +@ComponentV2 +struct Parent { + @Provider providerVar1: string = 'propVar1'; + @Provider providerVar2: number = 50; + @Provider providerVar3: boolean = true; + @Provider providerVar4: undefined = undefined; + @Provider providerVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..1a6fc86c70c278268b6d8c5b910b2561e84302cb --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-complex-type.ets @@ -0,0 +1,47 @@ +/* + * Copyright (C) 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 { ComponentV2 } from "@ohos.arkui.component" +import { Provider } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum StateType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@ComponentV2 +struct Parent { + @Provider paramVar1: Per = new Per(6); + @Provider paramVar2: Array = new Array(3,6,8); + @Provider paramVar3: StateType = StateType.TYPE3; + @Provider paramVar4: Set = new Set(new Array('aa', 'bb')); + @Provider paramVar5: boolean[] = [true, false]; + @Provider paramVar6: Array = new Array(new Per(7), new Per(11)); + @Provider paramVar7: Per[] = [new Per(7), new Per(11)]; + @Provider paramVar9: Date = new Date('2025-4-23'); + @Provider paramVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Provider paramVar11: string | number = 0.0; + @Provider paramVar12: Set | Per = new Per(6); + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets new file mode 100644 index 0000000000000000000000000000000000000000..6bb0cd890317bf011c6f641d1637c15287232551 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provider-and-consumer/provider-to-consumer.ets @@ -0,0 +1,72 @@ +/* + * Copyright (C) 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 { ComponentV2, DragEvent, Button, Column, Text, ForEach, Divider } from "@ohos.arkui.component" +import { Provider, Consumer, Local, ObservedV2, Trace } from "@ohos.arkui.stateManagement" + +@ObservedV2 +class User { + @Trace name: string; + @Trace age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } +} + +const data: User[] = [new User('Json', 10), new User('Eric', 15)]; + +@ComponentV2 +struct Parent { + @Provider('data') users: User[] = data; + + + build() { + Column() { + Child() + Button('add new user') + .onClick((e) => { + this.users.push(new User('Molly', 18)); + }) + Button('age++') + .onClick((e) => { + this.users[0].age++; + }) + Button('change name') + .onClick((e) => { + this.users[0].name = 'Shelly'; + }) + } + } +} + + +@ComponentV2 +struct Child { + @Consumer('data') users: User[] = []; + + build() { + Column() { + ForEach(this.users, (item: User) => { + Column() { + Text(`name: ${item.name}`).fontSize(30) + Text(`age: ${item.age}`).fontSize(30) + Divider() + } + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets new file mode 100644 index 0000000000000000000000000000000000000000..86703f8b58e981eff528f1fd518c952b5a66a735 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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 { Component, ComponentV2, BuilderParam } from "@ohos.arkui.component" +import { State, Require, Prop, Provide, Param } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + hello: string = 'hello'; + @State state1: boolean = false; + @Require select100: string; + @Require @State select0: number; + @Require @State select3: number | null; + @Require @State select4: undefined; + @Require @Prop select1: string; + @Require @Provide({ alias: '15' }) select2: string[]; + @Require @Provide({ alias: 't' }) select6: string[] | undefined | string; + @Require @BuilderParam builder: () => void; + + build() {} +} + +@ComponentV2 +struct V2222 { + @Require @Param select1: string; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets index 49331e647034ca169b243def6931cf493705f231..f77f9a04b97aaf59e97607b85e067610159eed97 100644 --- a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets @@ -13,36 +13,46 @@ * limitations under the License. */ -import { Component, $r, $rawfile, Column, Text, Image, TextInput, Select, SelectOption, Margin, ImageAnimator } from "@ohos.arkui.component" +import { Component, $r, $rawfile, Column, Text, Image, TextInput, Select, SelectOption, Margin, ImageAnimator, Resource } from "@ohos.arkui.component" @Component struct ResourceComponent { str1: string = 'app.media.ri' str2: string = 'app.photo2.png' + numbers: string[] = ['0','1','3','5','8'] + aboutToAppear() { + let arr: Array = new Array() + for (let i = 0; i < 5; i++) { + arr.push($r('app.string.app_name')) + } + for (let item of this.numbers) { + arr.push($r('app.string.app_name')) + } + } build() { Column() { Text($r('app.string.app_name')) - Image($rawfile('app.photo.png')) - TextInput({ text: $r('app.string.input_content') }) + Image($rawfile('app.mock.txt')) + TextInput({ text: $r('app.string.module_desc') }) Text($r(this.str1)) Text($r(this.str2)) Select(new Array( - { value: 'aaa', icon: $r("app.media.selection") }, - { value: 'bbb', icon: $r("app.media.selection") }, - { value: 'ccc', icon: $r("app.media.selection") }, - { value: 'ddd', icon: $r("app.media.selection") } + { value: 'aaa', icon: $r("app.media.background") }, + { value: 'bbb', icon: $r("app.media.background") }, + { value: 'ccc', icon: $r("app.media.background") }, + { value: 'ddd', icon: $r("app.media.background") } )) Image($r('app.media.app_icon')) .margin({ - top: $r('app.float.elements_margin_horizontal_m'), - bottom: $r('app.float.elements_margin_horizontal_l') + top: $r('app.float.page_text_font_size'), + bottom: $r('app.float.page_text_font_size') } as Margin) ImageAnimator().images([ { - src: $r('app.media.aaa') + src: $r('app.media.app_icon') }, { - src: $r('app.media.bbb') + src: $r('app.media.layered_image') }, ]) } diff --git a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets index 591311c7dd671e824d0ea6e39561dc4b8f4990ae..fca6a78a7096aef65412f00b4d3ebb233a7cc517 100644 --- a/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets @@ -20,7 +20,7 @@ let i: Resource = $r('app.string.app_name'); @Component struct ResourceComponent { private str: Resource = $r('app.string.app_name'); - private icon: Resource = $rawfile('app.photo.png'); + private icon: Resource = $rawfile('app.mock.txt'); build() { Column() { Text(this.str) diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets index 755a647b3260f375089b04a907d481ce03f82dde..547ed62ea0ace3b2aac355c254ae89cf206fd4cf 100644 --- a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets @@ -1,5 +1,20 @@ +/* + * Copyright (C) 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 { Component, Reusable} from "@ohos.arkui.component" -import { State, Link } from "@ohos.arkui.stateManagement" +import { State, Prop } from "@ohos.arkui.stateManagement" @Component struct MyStateSample { @@ -11,7 +26,7 @@ struct MyStateSample { @Component @Reusable struct Child { - @Link num: number = 1 + @Prop num: number = 1 @State num1: number = 2 build() { diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets index d66fe54519af51ebe4bae06aa87eb4bf3ed6cbf6..10bf9f803439f53758ecae5b653297554f1150d3 100644 --- a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry, Reusable, Column, Text, Button, ClickEvent, FontWeight} from "@ohos.arkui.component" import { State } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets index 298dc89559f07bb094d7b5fc8a395c01ba619f62..a99565933c57a52d676a18036f34035dd4b553ce 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" import { StorageLink, AppStorage } from "@ohos.arkui.stateManagement" @@ -12,7 +27,6 @@ class Data { AppStorage.setOrCreate('PropA', 47); AppStorage.setOrCreate('PropB', new Data(50)); - @Entry @Component struct Index { diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets index 9aed6364d9c83a3a6865d84ef559025e14fb4cb7..08b9113960050e21e0a8ec028dcf9983bec52514 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" import { StorageLink } from "@ohos.arkui.stateManagement" @@ -20,7 +35,6 @@ struct MyStateSample { @StorageLink('Prop3') dateA: Date = new Date('2021-08-08'); @StorageLink('Prop4') setA: Set = new Set(); @StorageLink('Prop5') mapA: Map = new Map(); - @StorageLink('Prop6') unionA: string | undefined = ""; @StorageLink('Prop7') classA: Person = new Person("John"); @StorageLink('Prop8') enumA: Status = Status.NotFound; diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets index e5afb4d19714d7e4e84d3eefb381e24cd376e867..a4219a46368068ad3c4c32197463fb72eb7e31ac 100644 --- a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-primitive-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" import { StorageLink } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..1951e82772cbed159254187e75dda626ae97e97b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-complex-type.ets @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StoragePropRef } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Component +struct MyStateSample { + @StoragePropRef('Prop1') arrayB: number[] = [1,2,3]; + @StoragePropRef('Prop2') objectB: Object = {}; + @StoragePropRef('Prop3') dateB: Date = new Date('2021-09-09'); + @StoragePropRef('Prop4') setB: Set = new Set(); + @StoragePropRef('Prop5') mapB: Map = new Map(); + @StoragePropRef('Prop7') classB: Person = new Person("Kevin"); + @StoragePropRef('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..e84494b669f09f4728b6e92586d3972b0494930b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop-ref/storageprop-ref-primitive-type.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StoragePropRef } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + @StoragePropRef('Prop1') numB: number = 43; + @StoragePropRef('Prop2') stringB: string = 'BB'; + @StoragePropRef('Prop3') booleanB: boolean = false; + @StoragePropRef('Prop4') undefinedB: undefined = undefined; + @StoragePropRef('Prop5') nullB: null = null; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets index bff353a175509fd8080dd3390ecb86ca3ec34831..dd25eb7fb008b724d932df2a6d2ed8e03c4ea069 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" import { StorageProp, AppStorage } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets index 5a770cf6ee43a829be0c528003838d4a61551ad3..50c2ea128a534ea463a12c780b1fa27ab051fe10 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins import { StorageProp } from "@ohos.arkui.stateManagement" @@ -20,7 +35,6 @@ struct MyStateSample { @StorageProp('Prop3') dateB: Date = new Date('2021-09-09'); @StorageProp('Prop4') setB: Set = new Set(); @StorageProp('Prop5') mapB: Map = new Map(); - @StorageProp('Prop6') unionB: string | undefined = ""; @StorageProp('Prop7') classB: Person = new Person("Kevin"); @StorageProp('Prop8') enumB: Status = Status.NotFound; diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets index d82a28552453804ec2f268b84a7baca461dc95c2..95d4ed58d42aaa7f76fbdfe8b1c6798ac2e22b6f 100644 --- a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets @@ -1,3 +1,18 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins import { StorageProp } from "@ohos.arkui.stateManagement" diff --git a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets index 6a85ab00ac447f337857d612b451c03e22197273..4c36212e7da874ab1268d3e671f7f6d69c867ee3 100644 --- a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets @@ -1,4 +1,19 @@ -import { Component, Entry } from "@ohos.arkui.component" +/* + * Copyright (C) 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 { Component, Entry, Column } from "@ohos.arkui.component" import { State, Prop, StorageLink, StorageProp, Link, Watch, ObjectLink, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" @Observed @@ -12,30 +27,32 @@ class A { struct MyStateSample { @State @Watch('stateOnChange') statevar: string = 'Hello World'; @Prop @Watch('propOnChange') propvar: string = 'Hello World'; - // @Link @Watch('linkOnChange') linkvar: string; + @Link @Watch('linkOnChange') linkvar: string; @StorageLink('prop1') @Watch('storageLinkOnChange') storagelinkvar: string = 'Hello World'; @StorageProp('prop2') @Watch('storagePropOnChange') storagepropvar: string = 'Hello World'; - // @ObjectLink @Watch('objectLinkOnChange') objectlinkvar: A; + @ObjectLink @Watch('objectLinkOnChange') objectlinkvar: A; @Provide @Watch('ProvideOnChange') providevar: string = 'Hello World'; stateOnChange(propName: string) {} propOnChange(propName: string) {} - // linkOnChange(propName: string) {} + linkOnChange(propName: string) {} storageLinkOnChange(propName: string) {} storagePropOnChange(propName: string) {} - // objectLinkOnChange(propName: string) {} + objectLinkOnChange(propName: string) {} ProvideOnChange(propName: string) {} build() { - Child() + Column() { + Child() + } } } @Component struct Child { - // @Consume @Watch('ConsumeOnChange') providevar: string; + @Consume @Watch('ConsumeOnChange') providevar: string; - // ConsumeOnChange(propName: string) {} + ConsumeOnChange(propName: string) {} build() {} } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets new file mode 100644 index 0000000000000000000000000000000000000000..eae3300b835288fced1703253c1fed14e344247c --- /dev/null +++ b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-griditem.ets @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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 { Text, Entry, Column, Component, $$, Grid, GridItem } from "@ohos.arkui.component" +import { State } from "@ohos.arkui.stateManagement" + +let c: boolean = false + +@Entry +@Component +struct MyStateSample { + @State boo: boolean = true + build() { + Column() { + Grid() { + GridItem() { + Text('nihao') + }.selected($$(this.boo)) + GridItem() { + Text('nihao') + }.selected($$(c)) + } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets similarity index 50% rename from arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets rename to arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets index e72c21ace10322a6fb0fdef80711a7d34bb98173..20ff29b73817bd2c3863fb251c82911024bb0efa 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets +++ b/arkui-plugins/test/demo/mock/double-dollar/double-dollar-toggle.ets @@ -13,41 +13,25 @@ * limitations under the License. */ -export declare enum FlexAlign { - Start = 0, - Center = 1, - End = 2, - SpaceBetween = 3, - SpaceAround = 4, - SpaceEvenly = 5 -} +import { Text, Column, Component, $$, Toggle, ToggleType } from "@ohos.arkui.component" +import { State } from "@ohos.arkui.stateManagement" -export declare enum HorizontalAlign { - Start = 0, - Center = 1, - End = 2 -} +let c: boolean[] = [true, false, true]; -export declare enum IlluminatedType { - NONE = 0, - BORDER = 1, - CONTENT = 2, - BORDER_CONTENT = 3, - BLOOM_BORDER = 4, - BLOOM_BORDER_CONTENT = 5 +class BooleanClass { + isOn: boolean = true; } -export declare enum Color { - White = 0, - Black = 1, - Blue = 2, - Brown = 3, - Gray = 4, - Green = 5, - Grey = 6, - Orange = 7, - Pink = 8, - Red = 9, - Yellow = 10, - Transparent = 11 +@Component +struct MyStateSample { + @State boo: boolean[] = [true, false, true]; + @State booClass: BooleanClass = new BooleanClass(); + + build() { + Column() { + Toggle({ type: ToggleType.Checkbox, isOn: $$(this.boo[0]) }) + Toggle({ type: ToggleType.Checkbox, isOn: $$(this.booClass.isOn) }) + Toggle({ type: ToggleType.Checkbox, isOn: $$(c[1]) }) + } + } } \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets b/arkui-plugins/test/demo/mock/entry/entry-only.ets similarity index 83% rename from arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets rename to arkui-plugins/test/demo/mock/entry/entry-only.ets index 9cc3b09e07de69770a6f394122713c2a0853a371..2d54e1bf2b273c0de5421035703a6d13acb48b82 100644 --- a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets +++ b/arkui-plugins/test/demo/mock/entry/entry-only.ets @@ -12,5 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Component, Entry } from "@ohos.arkui.component" -export type Resource = boolean; \ No newline at end of file +@Entry() +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets new file mode 100644 index 0000000000000000000000000000000000000000..2a98d667f93b20f8220c3647aeb204178f7eb615 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-false.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage', useSharedStorage: false}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets new file mode 100644 index 0000000000000000000000000000000000000000..e3c657bd36d062d2d483498973c76b155dc45539 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage-use-shared-storage-true.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage', useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets b/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1aefaefb7ba931b8e94e7954cca2c9fd24aa467 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/storage.ets @@ -0,0 +1,26 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({storage: 'myStorage'}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets new file mode 100644 index 0000000000000000000000000000000000000000..7be0659527f0d32af0e6c9869a3fbf8ef086b2ac --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-false.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({useSharedStorage: false}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets new file mode 100644 index 0000000000000000000000000000000000000000..f327cf40c9a00793584359984d4ca701c0e5b9f0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/localstorage/use-shared-storage-true.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() + +@Entry({useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} +} diff --git a/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets b/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets new file mode 100644 index 0000000000000000000000000000000000000000..c2d457f4c45d78d8e2db40c1e0cab3765198c067 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/route-name/route-name-storage-shared.ets @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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 { Component, Entry } from "@ohos.arkui.component" +import { LocalStorage } from "@ohos.arkui.stateManagement" + +const myStorage: ()=>LocalStorage = ()=>new LocalStorage() +@Entry({routeName: 'MyPage', storage: 'myStorage', useSharedStorage: true}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets b/arkui-plugins/test/demo/mock/entry/route-name/route-name.ets similarity index 79% rename from arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets rename to arkui-plugins/test/demo/mock/entry/route-name/route-name.ets index 9cb01ec593ed40cc9c98940e4d1b8dcc9b7bdd33..3a9fedb0676064cc6663fddcc204e770021742d3 100644 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets +++ b/arkui-plugins/test/demo/mock/entry/route-name/route-name.ets @@ -12,7 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Component, Entry } from "@ohos.arkui.component" -export * from "@ohos.arkui.stateManagement.common"; -export * from "@ohos.arkui.stateManagement.runtime"; -export * from "@ohos.arkui.stateManagement.storage"; +@Entry({routeName: 'MyPage'}) +@Component +struct MyStateSample { + build() {} + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/imports/import-struct.ets b/arkui-plugins/test/demo/mock/imports/import-struct.ets new file mode 100644 index 0000000000000000000000000000000000000000..d7cf79b5edd95207d7c12c77e0fe7c38722acf49 --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/import-struct.ets @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 { Component, Text } from "@ohos.arkui.component" +import { SimpleStruct } from "./utils/simple-struct" + +@Component +struct ImportStruct { + build() { + SimpleStruct(); + SimpleStruct({ message: "str1" }); + SimpleStruct() { + Text("a"); + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/imports/kit-import.ets b/arkui-plugins/test/demo/mock/imports/kit-import.ets new file mode 100644 index 0000000000000000000000000000000000000000..5cd7771b28e08fdfdd73c40373362704b6f01bd6 --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/kit-import.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 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 { Prop, Column, Entry } from "@kit.ArkUI"; +import { Text, Component, ClickEvent } from "@ohos.arkui.component"; +import { State } from "@ohos.arkui.stateManagement"; +import { Button } from "arkui.component.button"; +import hilog from "@ohos.hilog"; + +@Entry +@Component +struct A { + @State a: string = "str"; + @Prop b: string; + + build() { + Column() { + Button("button").onClick((e: ClickEvent) => { }) + Text("text").fontSize(20) + } + } +} diff --git a/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets b/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets new file mode 100644 index 0000000000000000000000000000000000000000..053de876ebb4deb4b630b5f7262c76a07f4992c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/imports/utils/simple-struct.ets @@ -0,0 +1,23 @@ +/* + * Copyright (C) 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 { Component } from "@ohos.arkui.component" + +@Component +export struct SimpleStruct { + message: string = "str"; + + build() {} +} diff --git a/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets index 54263a55218737151cc173c8a8ef20ccddb6a699..1db0074a76a549f58dd45d7ee78a04cbf23e5c25 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function memo_arg_call( arg1: number, diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets b/arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets similarity index 33% rename from arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets rename to arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets index 274947957808f9524d2fbf8746fffd2dd6934570..1ab1a5eba438c895d59b645d3adf02d219c848c7 100644 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.common.d.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/complex-memo-intrinsic.ets @@ -13,49 +13,62 @@ * limitations under the License. */ -@Retention({policy: "SOURCE"}) -export declare @interface State {}; +import { memo } from "arkui.stateManagement.runtime"; -@Retention({policy: "SOURCE"}) -export declare @interface Prop {}; +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} -@Retention({policy: "SOURCE"}) -export declare @interface Link {}; +interface IA { + ccc: boolean; +} -@Retention({policy: "SOURCE"}) -export declare @interface Observed {}; +class A implements IA { + bbb: Map = new Map(); + ddd: Map = new Map(); + ccc: boolean = false; -@Retention({policy: "SOURCE"}) -export declare @interface Track {}; + aaa(value: number): void {} +} -@Retention({policy: "SOURCE"}) -export declare @interface ObjectLink {}; +export type SimpleArray = Array | ReadonlyArray | Readonly>; -@Retention({policy: "SOURCE"}) -export declare @interface StorageProp { value: string }; +@memo_intrinsic +export declare function factory(compute: () => Value): Value; -@Retention({policy: "SOURCE"}) -export declare @interface StorageLink { value: string }; +export function cb(callback?: () => void) { + if (callback) return; +} -@Retention({policy: "SOURCE"}) -export declare @interface LocalStorageProp { value: string }; +@memo_intrinsic +export function impl( + @memo + style: ((attributes: IA) => void) | undefined, + arr: SimpleArray, + err: string = 'error message' +): void { + const s = factory(() => { + return new A(); + }); + s.aaa(arr.length); + style?.(s); + if (!s.bbb.get('some_key')) { + throw new Error(err); + } + if (s.ccc) { + cb(() => + s.ddd.forEach((s: number, t: string) => { + console.log(err); + return; + }) + ); + } else { + return; + } +} -@Retention({policy: "SOURCE"}) -export declare @interface LocalStorageLink { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Provide { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Consume { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Watch { value: string }; - -@Retention({policy: "SOURCE"}) -export declare @interface Require {}; - -export declare class UIUtils { - static getTarget(source: T): T; - static makeObserved(source: T): T; +class Use { + @memo test() { + const style = @memo (attributes: IA) => {}; + const arr = [1, 2, 3, 4]; + impl(style, arr); + } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets b/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets index ac8324d2211e079a3f6cbec9f2d1c26ceb53a123..9b54856e512b5a835d63ed2a53d91ea11d4db03a 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo declare function funcA(): void; @@ -29,7 +29,26 @@ function funcB(): void { funcA(); } +interface MemoBuilder { + @memo builder: () => void +} + +@memo +function funcWithMemoBuilder(@memo memo_arg: MemoBuilder): void {} + +function funcWithArg(arg: () => void): void {} + +function func(): void {} + @memo () => { funcA(); + funcWithMemoBuilder({ + builder: () => { + funcWithArg(() => { + func(); + return; + }); + } + }); } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets b/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets index 313164b28bbb3b45af10444c109993013ce7350a..40759ede45a377cb8fa44059707f46eb23b8cd6c 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/inner-functions.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function foo() { } diff --git a/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets b/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets new file mode 100644 index 0000000000000000000000000000000000000000..38f146018c0a4ec38b5456efd91e5a647acaf7be --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/functions/internal-memo-arg.ets @@ -0,0 +1,101 @@ +/* + * Copyright (C) 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 { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; +import { IncrementalNode } from "@koalaui.runtime.tree.IncrementalNode"; +import { ControlledScope, StateManager } from "@koalaui.runtime.states.State"; + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} +@Retention({policy:"SOURCE"}) @interface memo_entry {} + +export declare function __context(): __memo_context_type +export declare function __id(): __memo_id_type + +@memo_entry() export function memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: (()=> R)): R { + return entry(); +} + +@memo_entry() export function memoEntry1(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((arg: T)=> R), arg: T): R { + return entry(arg); +} + +@memo_entry() export function memoEntry2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((arg1: T1, arg2: T2)=> R), arg1: T1, arg2: T2): R { + return entry(arg1, arg2); +} + +export class MemoCallbackContext { + private readonly context: __memo_context_type; + + private readonly id: __memo_id_type; + + private constructor(context: __memo_context_type, id: __memo_id_type) { + this.context = context; + this.id = id; + } + + @memo() public static Make(): MemoCallbackContext { + return new MemoCallbackContext(__context(), __id()); + } +} + +@memo_intrinsic() export function contextLocalValue(name: string): Value { + return __context().valueBy(name); +} + +@memo_intrinsic() export function contextLocalScope(name: string, value: Value, @memo() content: (()=> void)) { + const scope = __context().scope(__id(), 1); + scope.param(0, value, undefined, name, true); + if (scope.unchanged) { + scope.cached; + } else { + content(); + scope.recache(); + } +} + +@memo_intrinsic() export function NodeAttach(create: (()=> Node), @memo() update: ((node: Node)=> void), reuseKey?: string): void { + const scope = __context().scope(__id(), 0, create, undefined, undefined, undefined, reuseKey); + if (scope.unchanged) { + scope.cached; + } else { + try { + if (!reuseKey) { + update((__context().node as Node)) + } else { + memoEntry(__context(), 0, (() => { + update((__context().node as Node)); + })) + } + } finally { + scope.recache(); + } + } +} + +@memo_intrinsic() export function rememberControlledScope(invalidate: (()=> void)): ControlledScope { + return __context().controlledScope(__id(), invalidate); +} + +@memo() export function Repeat(count: int, @memo() action: ((index: int)=> void)) { + for (let i = 0;((i) < (count));(i++)) { + memoEntry1(__context(), i, action, i); + } +} + +export class CustomComponent { + @memo() public static _instantiateImpl(): void { + const context: StateManager = (__context() as StateManager); + } +} diff --git a/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets index cc0f993ec1eb1bb3ee0cea0548a4ab2e8fd8985a..fca54fcc759b39492c91e9636aa77b2399764ff6 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function funcNum(): number { diff --git a/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets b/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets index be715df9520f12300b9ccab2938677caa7572720..fb4e922ace484c2f51b04bd95a69fa51135b73d3 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo type ItemBuilder = (item: Item) => void; diff --git a/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets index 840c84aaf33c113cde341e668480a23f06dd6ed7..5605e2b68528cfd0c7059d89672b9f3bce69da85 100644 --- a/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo function func(): void { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets index def28807a683db299c935b25e938d18a68fa300b..455fb68438738748a2eab6c3ee1362b6df4186a4 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; (arg: (()=>void)) => {}(() => {}); diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets index 36bc7d82fa0619651dfb6956a10d68e33b718a7d..4ade068c9d96371c0f1c69234d842196096d851a 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class B { @memo internal_call(): B { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets index 08d252f583d623b7ad40503313053a66f4254a9c..1bfdb18b91d4b8d74eda0378c1d582722b904953 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { @memo foo(p?: ()=>void): void {} diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets index fb757d8b65bc190fb1d3a7e8eb24e76131d8dd86..2f5afae38d6f4aa2b77c1575cdcf6a995b13d48e 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; @memo (): void => { diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets index b54c9c6c215117441391ad22670444ff65e684ef..556aba2b53c44be09e8401ec1b53a4fefffb168c 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets @@ -13,22 +13,29 @@ * limitations under the License. */ - import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class Person { constructor() {} } +@memo function fullName(this: Person, @memo arg?: () => void): void { return; } class A {} +@memo type F1 = (this: A, @memo arg?: () => void) => void; + +@memo type F2 = (a: A, @memo arg?: () => void) => void; +@memo function foo(this: A, @memo arg?: () => void): void {} + +@memo function goo(a: A, @memo arg?: () => void): void {} @memo @@ -37,11 +44,7 @@ function goo(a: A, @memo arg?: () => void): void {} x.fullName(() => {}); let f1: F1 = foo; - f1 = goo; - let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); a.f1(() => {}); diff --git a/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets b/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets index 5f799140fdd1d8bfa485431799703a68446d6c88..25903f0435824055e305a26e3cc35c4c0d06c08e 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class Test { @memo lambda_arg(@memo arg: () => void) { diff --git a/arkui-plugins/test/demo/mock/memo/methods/callable.ets b/arkui-plugins/test/demo/mock/memo/methods/callable.ets index 2f4e2a401687e34c53dbd0c53e94a190edb6451f..bebbb75019c01d90cec62c17232ca22e16d66002 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/callable.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/callable.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { @memo diff --git a/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets b/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets index e28c853612e36b399f056c161e0d7a25842dd016..121e1568d790c188fed431cb9a8c618cf1d56f21 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; declare abstract class A { @memo diff --git a/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets index 7ef4cdb0e194404f46e81730e10ab61012bd81f2..5dcd4c2920d870d0f7c9c3f7505365558ba14261 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; export declare function __context(): __memo_context_type export declare function __id(): __memo_id_type diff --git a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets index 06df30297b35c8a7bc5d5f94fcabdcbef5457658..4e8053d506e6620b8fa153035afadfb4c3f2374d 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets @@ -13,10 +13,11 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement.runtime"; @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} export declare function __context(): __memo_context_type export declare function __id(): __memo_id_type @@ -55,6 +56,12 @@ class Test { return entry() } } + + @memo memo_skip_args(arg1: number, @memo_skip arg2: string, @memo_skip arg3: @memo () => void): string { + let a = arg1; + arg3(); + return arg2; + } } class Use { diff --git a/arkui-plugins/test/demo/mock/memo/methods/void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets index ed3339dd17a1768970424b8401936d2716d0d8a3..d2ee07cd7e93b8b41fda05fe6b3fbb99b7c3ba58 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/void-method.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { x: int diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets index 725d2702b15a0360ac41262b23839816b65e4b1e..a349e0450c4956e6a0a220f7ab61c47a0ebc9866 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { @memo a: () => void diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets index 9fe1e5dc4ff19dfd5fd5ea76457b89863cbc6e5f..79c72508bb5bed5f84aec0d417feff5052e868af 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; class A { arg: () => void diff --git a/arkui-plugins/test/demo/mock/memo/properties/implements.ets b/arkui-plugins/test/demo/mock/memo/properties/implements.ets index ad61b2a7a49d2877344e5b1aff901f3739279579..cdf8c6644bbca8d22526707e9cf4d68604003e87 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/implements.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/implements.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { @memo prop: (() => void) | undefined diff --git a/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets b/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets index c46a181e573a7bb02bb8f85c126744ebd7361a48..80a9332fd4409f6f40c01232599b0aadb3dee521 100644 --- a/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets +++ b/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" +import { memo } from "arkui.stateManagement.runtime"; interface A { arg: () => void diff --git a/arkui-plugins/test/demo/mock/resource/ResourceTable.txt b/arkui-plugins/test/demo/mock/resource/ResourceTable.txt new file mode 100644 index 0000000000000000000000000000000000000000..6c0c36582e7895d8bd429ff1b6e98d965e14619c --- /dev/null +++ b/arkui-plugins/test/demo/mock/resource/ResourceTable.txt @@ -0,0 +1,13 @@ +string EntryAbility_desc 0x01000002 +string EntryAbility_label 0x01000003 +string app_name 0x01000000 +string module_desc 0x01000004 +color start_window_background 0x01000005 +float page_text_font_size 0x01000006 +media app_icon 0x01000001 +media background 0x01000007 +media foreground 0x01000008 +media layered_image 0x01000009 +media startIcon 0x0100000a +profile backup_config 0x0100000b +profile main_pages 0x0100000c \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt b/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt new file mode 100644 index 0000000000000000000000000000000000000000..1eb866e0e1f17b2af95ac2dbb7fee38d688e7612 --- /dev/null +++ b/arkui-plugins/test/demo/mock/resource/rawfile/mock.txt @@ -0,0 +1 @@ +mocking rawfile \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets b/arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets similarity index 41% rename from arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets rename to arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets index 4a0e94d6f089aab4442c94ca5d4f960aa70240af..ab94ff3f3581355cc7c0112c4eca972e889db9aa 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets +++ b/arkui-plugins/test/demo/mock/wrap-builder/builder-in-generic.ets @@ -13,20 +13,36 @@ * limitations under the License. */ -export declare class StyledString { - constructor(value: string /*| ImageAttachment | CustomSpan, styles?: Array*/); +import { Builder, Text, Color, WrappedBuilder, wrapBuilder, Entry, Component, Row, ForEach } from '@ohos.arkui.component'; +import { State } from '@ohos.arkui.stateManagement'; - readonly length: number; +@Builder +function MyBuilder(value: string, size: number) { + Text(value) + .fontSize(size) +} - getString(): string; - // getStyles(start: number, length: number, styledKey?: StyledStringKey): Array; - equals(other: StyledString): boolean; - subStyledString(start: number, length?: number): StyledString; +@Builder +function YourBuilder(value: string, size: number) { + Text(value) + .fontSize(size) + .fontColor(Color.Pink) +} - static fromHtml(html: string): Promise; - static toHtml(styledString: StyledString): string; - // static marshalling(styledString: StyledString, callback: StyledStringMarshallCallback): ArrayBuffer; - // static unmarshalling(buffer: ArrayBuffer, callback: StyledStringUnmarshallCallback): Promise; - // static marshalling(styledString: StyledString): ArrayBuffer; - // static unmarshalling(buffer: ArrayBuffer): Promise; +let globalBuilder: (@Builder (value: string, size: number) => void) = MyBuilder; +let builderArr: (@Builder (value: string, size: number) => void)[] = [MyBuilder, YourBuilder] + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + globalBuilder(this.message, 50); + ForEach(builderArr, (item: (@Builder (value: string, size: number) => void)) => { + item('Hello World', 30); + }) + }.height('100%') + } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets b/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets new file mode 100644 index 0000000000000000000000000000000000000000..8ec8bd8a67acc1b77e2066f459f0113395dafde2 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/init-with-builder.ets @@ -0,0 +1,33 @@ +/* + * Copyright (C) 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 { Component, Text, WrappedBuilder, wrapBuilder, Builder, Column } from "@kit.ArkUI" + +@Builder +function myBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +type MyBuilderFuncType = @Builder (value: string, size: number) => void; +let globalBuilder: WrappedBuilder = wrapBuilder(myBuilder); + +@Component +struct ImportStruct { + build() { + Column() { + globalBuilder.builder('hello', 50) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f82d420d8b9179c09d69efe8fcd86f4f8711ea9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-generic.ets @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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 { Builder, Text, Color, WrappedBuilder, wrapBuilder, Entry, Component, Row, ForEach } from '@ohos.arkui.component'; +import { State } from '@ohos.arkui.stateManagement'; + +@Builder +function MyBuilder(value: string, size: number) { + Text(value) + .fontSize(size) +} + +@Builder +function YourBuilder(value: string, size: number) { + Text(value) + .fontSize(size) + .fontColor(Color.Pink) +} + +let globalBuilder: WrappedBuilder<@Builder (value: string, size: number) => void> = wrapBuilder(MyBuilder); +let builderArr: WrappedBuilder<@Builder (value: string, size: number) => void>[] = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)] +let wrappedBuilder1: WrappedBuilder<@Builder (value: string, size: number) => void> = wrapBuilder<@Builder (value: string, size: number) => void>(MyBuilder); +let wrappedBuilder2: WrappedBuilder<@Builder (value: string, size: number) => void> = new WrappedBuilder<@Builder (value: string, size: number) => void>(MyBuilder); + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + globalBuilder.builder(this.message, 50); + ForEach(builderArr, (item: WrappedBuilder<@Builder (value: string, size: number) => void>) => { + item.builder('Hello World', 30); + }) + }.height('100%') + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets new file mode 100644 index 0000000000000000000000000000000000000000..f92db0bc0ef961bc7bdb8719a1f8925186a14a27 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-in-ui.ets @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 { Component, Text, WrappedBuilder, wrapBuilder, Builder, Column, ForEach } from "@kit.ArkUI" + +@Builder +function myBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +@Builder +function yourBuilder(value: string, size: number) { + Text(value).fontSize(size) +} + +type MyBuilderFuncType = @Builder (value: string, size: number) => void; +const globalBuilderArr: WrappedBuilder[] = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@Component +struct ImportStruct { + @Builder testBuilder() { + ForEach(globalBuilderArr, (item: WrappedBuilder) => { + item.builder('hello world', 39) + }) + } + + build() { + Column() { + this.testBuilder() + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets new file mode 100644 index 0000000000000000000000000000000000000000..4604a07a5eb004d7e1ed133ce6dfca80431e8087 --- /dev/null +++ b/arkui-plugins/test/demo/mock/wrap-builder/wrap-builder-with-lambda.ets @@ -0,0 +1,45 @@ +/* + * Copyright (C) 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 { Observed, Builder, Entry, Component, State } from "@kit.ArkUI"; +import { WrappedBuilder, wrapBuilder } from "@kit.ArkUI"; +import { Column, Text, Button, ClickEvent } from "@kit.ArkUI"; + +@Observed +class Tmp { + paramA2: string = 'hello'; +} + +@Builder function overBuilder(param: () => Tmp) { + Column() { + Text(`wrapBuildervalue:${param().paramA2}`) + } +} + +type MyBuilderFuncType = @Builder (param: () => Tmp) => void; +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +@Component +struct Parent { + @State label: Tmp = new Tmp(); + build() { + Column() { + wBuilder.builder(() => { return { paramA2: this.label.paramA2 } }) + Button('Click me').onClick((e: ClickEvent) => { + this.label.paramA2 = 'ArkUI'; + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets b/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets deleted file mode 100644 index aa644d5eddd01b0abdefc34aa0fc206c30e01d72..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/demo/mock/xcomponent/xcomponent-basic.ets +++ /dev/null @@ -1,19 +0,0 @@ -import {Component, Flex, XComponent, FlexDirection, XComponentType, Entry, XComponentController, ItemAlign, FlexAlign, XComponentParameter} from '@ohos.arkui.component' - -@Entry -@Component -struct Index { - myXComponentController: XComponentController = new XComponentController(); - build() { - Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { - XComponent({ - id: 'xComponentId', - type: XComponentType.TEXTURE, - libraryname: 'nativerender', - controller: this.myXComponentController - } as XComponentParameter) - } - .width('100%') - .height('100%') - } -} \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets b/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets deleted file mode 100644 index 5ba7ef75477458956a6042bd3a1b760903fcd89c..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/local/@ohos.arkui.component.text.d.ets +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 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 { Resource } from "@ohos.arkui.external.resource"; -import { StyledString } from "@ohos.arkui.component.styledString"; -import { ComponentBuilder, CommonMethod } from "@ohos.arkui.component.common"; -import { ResourceColor, Length } from "@ohos.arkui.component.units"; -import { memo } from '@ohos.arkui.stateManagement.runtime'; - -export declare class TextController { - closeSelectionMenu(): void; - setStyledString(value: StyledString): void; - - // getLayoutManager(): LayoutManager; -} - -export declare interface TextOptions { - controller: TextController; -} - -export declare interface TextAttribute extends CommonMethod { - // @memo - // font(value: Font, options?: FontSettingOptions): this; - @memo - fontColor(value: ResourceColor): this; - @memo - fontSize(value: number | string | Resource): this; - @memo - minFontSize(value: number | string | Resource): this; - @memo - maxFontSize(value: number | string | Resource): this; - @memo - minFontScale(scale: number | Resource): this; - @memo - maxFontScale(scale: number | Resource): this; - // @memo - // fontStyle(value: FontStyle): this; - // @memo - // fontWeight(value: number | FontWeight | string): this; - // @memo - // fontWeight(weight: number | FontWeight | string, options?: FontSettingOptions): this; - // @memo - // lineSpacing(value: LengthMetrics): this; - // @memo - // textAlign(value: TextAlign): this; - @memo - lineHeight(value: number | string | Resource): this; - // @memo - // textOverflow(options: TextOverflowOptions): this; - @memo - fontFamily(value: string | Resource): this; - @memo - maxLines(value: number): this; - // @memo - // decoration(value: DecorationStyleInterface): this; - @memo - letterSpacing(value: number | string): this; - // @memo - // textCase(value: TextCase): this; - @memo - baselineOffset(value: number | string): this; - // @memo - // copyOption(value: CopyOptions): this; - @memo - draggable(value: boolean): this; - // @memo - // textShadow(value: ShadowOptions | Array): this; - // @memo - // heightAdaptivePolicy(value: TextHeightAdaptivePolicy): this; - @memo - textIndent(value: Length): this; - // @memo - // wordBreak(value: WordBreak): this; - // @memo - // lineBreakStrategy(strategy: LineBreakStrategy): this; - @memo - onCopy(callback: (value: string) => void): this; - @memo - selection(selectionStart: number, selectionEnd: number): this; - @memo - caretColor(color: ResourceColor): this; - @memo - selectedBackgroundColor(color: ResourceColor): this; - // @memo - // ellipsisMode(value: EllipsisMode): this; - @memo - enableDataDetector(enable: boolean): this; - // @memo - // dataDetectorConfig(config: TextDataDetectorConfig): this; - // @memo - // bindSelectionMenu(spanType: TextSpanType, content: CustomBuilder, responseType: TextResponseType, - // options?: SelectionMenuOptions): this; - @memo - onTextSelectionChange(callback: (selectionStart: number, selectionEnd: number) => void): this; - @memo - fontFeature(value: string): this; - // @memo - // marqueeOptions(options: Optional): this; - // @memo - // onMarqueeStateChange(callback: Callback): this; - @memo - privacySensitive(supported: boolean): this; - // @memo - // textSelectable(mode: TextSelectableMode): this; - // @memo - // editMenuOptions(editMenu: EditMenuOptions): this; - @memo - halfLeading(halfLeading: boolean): this; - @memo - enableHapticFeedback(isEnabled: boolean): this; -} - -@memo -@ComponentBuilder -export declare function Text ( - value?: string | Resource, - options?: TextOptions, - @memo - content?: () => void -): TextAttribute; diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets b/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets deleted file mode 100644 index f70c0cb7f5ec72d10ec63b245007a17403df611c..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.runtime.d.ets +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 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 { LocalStorage } from "@ohos.arkui.stateManagement.storage" - -// From incremental engine -@Retention({policy: "SOURCE"}) -export declare @interface memo {}; - -export type __memo_context_type = StateContext; -export type __memo_id_type = MemoCallSiteKey; - -export type MemoCallSiteKey = int; - -export declare interface Disposable { - readonly disposed: boolean; - dispose(): void; -} - -export declare interface State { - readonly modified: boolean; - readonly value: T; -} - -export declare interface MutableState extends Disposable, State { - value: T; -} - -export type Equivalent = (oldV: T, newV: T) => boolean; - -export declare interface InternalScope { - readonly unchanged: boolean; - readonly cached: Value; - recache(newValue?: Value): Value; - param(index: int, value: T, equivalent?: Equivalent, name?: string, contextLocal?: boolean): State; -} - -export declare interface StateContext { - scope(id: MemoCallSiteKey, paramCount?: int): InternalScope; -} - -// From Arkoala -export declare function propState(value?: T): MutableState; -export declare function objectLinkState(value?: T): MutableState; -export declare function stateOf(value: T): MutableState; -export declare function contextLocalStateOf(value: T, key: () => T): MutableState; -export declare function contextLocal(value: T): MutableState; -export declare function observableProxy(value: T): T; -export declare function StorageLinkState(storage: LocalStorage, name: string, value: T): MutableState -export declare function AppStorageLinkState(name: string, value: T): MutableState; \ No newline at end of file diff --git a/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets b/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets deleted file mode 100644 index a7257c839afaadfda0ce7c00942710078cb959aa..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.storage.d.ets +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 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. - */ - -export declare interface StorageProperty { - key: string; - defaultValue: number | string | boolean | Object; -} - -export type PersistPropsOptions = StorageProperty; - -export declare interface AbstractProperty { - info(): string; - get(): T; - set(newValue: T): void; -} - -export declare interface SubscribedAbstractProperty extends AbstractProperty { - aboutToBeDeleted(): void; -} - -export declare class LocalStorage { - static getShared(): LocalStorage | undefined; - - constructor(initializingProperties?: StorageProperty[]); - - has(propName: string): boolean; - - keys(): IterableIterator; - - size(): int; - - get(propName: string): T | undefined; - - set(propName: string, newValue: T): boolean; - - setOrCreate(propName: string, newValue?: T): boolean; - - ref(propName: string): AbstractProperty | undefined; - - setAndRef(propName: string, defaultValue: T): AbstractProperty; - - link(propName: string): SubscribedAbstractProperty | undefined; - - setAndLink(propName: string, defaultValue: T): SubscribedAbstractProperty; - - prop(propName: string): SubscribedAbstractProperty | undefined; - - setAndProp(propName: string, defaultValue: T): SubscribedAbstractProperty; - - delete(propName: string): boolean; - - clear(): boolean; -} - -export declare class AppStorage { - static has(propName: string): boolean; - - static keys(): IterableIterator; - - static size(): int; - - static get(propName: string): T | undefined; - - static set(propName: string, newValue: T): boolean; - - static setOrCreate(propName: string, newValue?: T): boolean; - - static ref(propName: string): AbstractProperty | undefined; - - static setAndRef(propName: string, defaultValue: T): AbstractProperty; - - static link(propName: string): SubscribedAbstractProperty | undefined; - - static setAndLink(propName: string, defaultValue: T): SubscribedAbstractProperty; - - static prop(propName: string): SubscribedAbstractProperty | undefined; - - static setAndProp(propName: string, defaultValue: T): SubscribedAbstractProperty; - - static delete(propName: string): boolean; - - static clear(): boolean; -} - -export declare class PersistentStorage { - - static persistProp(key: string, defaultValue: T): void; - - static deleteProp(key: string): void; - - static persistProps(props: PersistPropsOptions[]): void; - - static keys(): Array; -} - -export declare interface EnvPropsOptions { - key: string; - defaultValue: number | string | boolean; -} - -export declare class Environment { - static envProp(key: string, value: S): boolean; - - static envProps(props: EnvPropsOptions[]): void; - - static keys(): Array; -} \ No newline at end of file diff --git a/arkui-plugins/test/localtest_config.js b/arkui-plugins/test/localtest_config.js index 87fd2e7eec4599b2e9449a9913998b7bf3710527..00d2cfb5157e4473f7faaefc6d58dacdac16a860 100644 --- a/arkui-plugins/test/localtest_config.js +++ b/arkui-plugins/test/localtest_config.js @@ -16,6 +16,10 @@ const fs = require('fs'); const path = require('path'); +function changePathToAbsPath(p) { + return path.resolve(p); +} + // 获取当前目录 const currentDirectory = process.cwd(); let workSpace = currentDirectory; @@ -27,25 +31,63 @@ const jsonFilePath = path.join(__dirname, 'demo/localtest/build_config_template. const outJsonFilePath = path.join(__dirname, 'demo/localtest/build_config.json'); try { - // 读取 JSON 文件内容 - const data = fs.readFileSync(jsonFilePath, 'utf8'); - const jsonData = JSON.parse(data); - console.log(jsonData) - // 处理 baseUrl 字段 - if (jsonData.buildSdkPath) { - jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); - } - - // 处理 plugins 字段 - if (jsonData.plugins.ui_plugin) { - jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); - } - if (jsonData.plugins.memo_plugin) { - jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); - } - - // 将修改后的内容写回 JSON 文件 - fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); + // 读取 JSON 文件内容 + const data = fs.readFileSync(jsonFilePath, 'utf8'); + const jsonData = JSON.parse(data); + console.log(jsonData) + // 处理 baseUrl 字段 + if (jsonData.buildSdkPath) { + jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); + } + + // 处理 plugins 字段 + if (jsonData.plugins.ui_syntax_plugin) { + jsonData.plugins.ui_syntax_plugin = jsonData.plugins.ui_syntax_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.ui_plugin) { + jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.memo_plugin) { + jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); + } + + // compileFiles + if (jsonData.compileFiles) { + jsonData.compileFiles = jsonData.compileFiles.map((file) => changePathToAbsPath(file)); + } + + // entryFiles + if (jsonData.entryFiles) { + jsonData.entryFiles = jsonData.entryFiles.map((file) => changePathToAbsPath(file)); + } + + // moduleRootPath + if (jsonData.moduleRootPath) { + jsonData.moduleRootPath = changePathToAbsPath(jsonData.moduleRootPath); + } + + // sourceRoots + if (jsonData.sourceRoots) { + jsonData.sourceRoots = jsonData.sourceRoots.map((file) => changePathToAbsPath(file)); + } + + // loaderOutPath + if (jsonData.loaderOutPath) { + jsonData.loaderOutPath = changePathToAbsPath(jsonData.loaderOutPath); + } + + // loaderOutPath + if (jsonData.cachePath) { + jsonData.cachePath = changePathToAbsPath(jsonData.cachePath); + } + + // appModuleJsonPath + if (jsonData.aceModuleJsonPath) { + jsonData.aceModuleJsonPath = changePathToAbsPath(jsonData.aceModuleJsonPath); + } + + // 将修改后的内容写回 JSON 文件 + fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); } catch (error) { - console.error('处理 JSON 文件时出错:', error); + console.error('处理 JSON 文件时出错:', error); } \ No newline at end of file diff --git a/arkui-plugins/test/package.json b/arkui-plugins/test/package.json index 07123fbd761be13ee570397e4994ffff75d65231..46947c865d37f49fcd57c1804898a9d9a0288558 100644 --- a/arkui-plugins/test/package.json +++ b/arkui-plugins/test/package.json @@ -1,7 +1,6 @@ { "name": "arkui-plugins-test", "version": "1.0.0", - "description": "", "private": true, "scripts": { "compile:ohos": "node $INIT_CWD/../../../../arkcompiler/ets_frontend/ets2panda/driver/build-system/dist/entry.js ./demo/hello_world/build_config.json", @@ -10,7 +9,15 @@ "clean:localtest": "rm -rf dist", "clean:test": "rm -rf generated", "clean:all": "npm run clean:localtest && npm run clean:test", + "mem": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib LD_BIND_NOW=1 /usr/local/valgrind-3.25.1/bin/valgrind --tool=massif --pages-as-heap=yes --heap=yes --alloc-fn='libes2panda_public.so*' --alloc-fn='*es2panda.node*' node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "local": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "inspect": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node --inspect-brk --expose_gc --no-turbo-inlining $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "clinic_mem": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic heapprofiler --node-arguments='--heapsnapshot-on-signal' -- node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "mem1": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic flame --mem --collect-only -- node --expose_gc --heap-prof --trace-gc --track-heap-objects $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "mem2": "rm -rf dist && node localtest_config.js && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib clinic flame --mem-prof -- node --no-node-snapshot --no-deprecation $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", + "valgrind": "rm -rf dist && node localtest_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib LD_BIND_NOW=1 /usr/local/valgrind-3.25.1/bin/valgrind --tool=massif --pages-as-heap=yes --heap=yes --alloc-fn='libes2panda_public.so*' --alloc-fn='*es2panda.node*' node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "test": "npm run clean:all && npm run compile:plugins && cd .. && npm run test", + "test:gdb": "npm run clean:all && npm run compile:plugins && cd .. && npm run test:gdb", "localtest": "rm -rf dist && node localtest_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "localtest_gdb": "LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib gdb --args node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_config.json", "localtest_decl": "rm -rf dist && node localtest_decl_config.js && npm run compile:plugins && LD_LIBRARY_PATH=$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/localtest/build_decl_config.json", diff --git a/arkui-plugins/test/test.log b/arkui-plugins/test/test.log deleted file mode 100644 index 984a29adfe735c5a5e2085b0494ad066ed1beadd..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/test.log +++ /dev/null @@ -1,372 +0,0 @@ - -> build_system_test@1.0.0 compile:ohos_sdk -> node /home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/hello_world/build_config.json - -[ - '/home/wuhaibin/.nvm/versions/node/v23.8.0/bin/node', - '/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js', - './demo/hello_world/build_config.json' -] -Updated PATH: /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/node_modules/.bin:/home/wuhaibin/newcode/oh/developtools/node_modules/.bin:/home/wuhaibin/newcode/oh/node_modules/.bin:/home/wuhaibin/newcode/node_modules/.bin:/home/wuhaibin/node_modules/.bin:/home/node_modules/.bin:/node_modules/.bin:/home/wuhaibin/.nvm/versions/node/v23.8.0/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/home/wuhaibin/.local/bin:/home/wuhaibin/bin:/home/wuhaibin/.nvm/versions/node/v23.8.0/bin:/home/wuhaibin/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Users/wuhaibin/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/wuhaibin/AppData/Local/Programs/Microsoft VS Code/bin:/snap/bin:/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib -Loaded plugin: ui-plugin { uiTransform: [Function: uiTransform] } [Function: uiTransform] -Loaded plugin: memo-plugin { unmemoizeTransform: [Function: unmemoizeTransform] } [Function: unmemoizeTransform] -ets2pandaCmd: _ --extension ets --arktsconfig /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/cache/entry/arktsconfig.json --output /home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/cache/entry/a.abc --debug-info ./demo/hello_world/entry/a.ets -[TS WRAPPER] CREATE CONFIG -InitModule: es2panda - -[TS WRAPPER] PROCEED TO STATE: 1 -es2panda proceedToState parsed -[TS WRAPPER] GET AST FROM CONTEXT -executing plugin: ui-plugin -[UI PLUGIN] AFTER PARSED ENTER -[AFTER PARSED SCRIPT]: -import { StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -function isTrue(): string { - return "aa"; -} - -final class MyStateSample extends StructBase { - public aaa: string = isTrue(); - - public build() { - Column(){ - Text("Hello World!"); - Text((this).aaa); - Button("change"); - }; - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => new MyStateSample()), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[UI PLUGIN] AFTER PARSED EXIT -plugin parsed finished -[TS WRAPPER] GET AST FROM CONTEXT -[TS WRAPPER] DESTROY AND RECREATE -[TS WRAPPER] PROCEED TO STATE: 4 -es2panda proceedToState checked -[TS WRAPPER] GET AST FROM CONTEXT -executing plugin: ui-plugin -[UI PLUGIN] AFTER CHECKED ENTER -[AFTER STRUCT SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - @memo()public __initializeStruct(initializers?: __Options_MyStateSample, @memo()content?: (()=> void)): void {} - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - @memo()protected _build(@memo()style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo()content: (()=> void) | undefined, initializers?: __Options_MyStateSample): void { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(((instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - })); - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[UI PLUGIN] AFTER CHECKED EXIT -executing plugin: memo-plugin -[MEMO PLUGIN] AFTER CHECKED ENTER -[BEFORE MEMO SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - @memo()public __initializeStruct(initializers?: __Options_MyStateSample, @memo()content?: (()=> void)): void {} - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - @memo()protected _build(@memo()style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo()content: (()=> void) | undefined, initializers?: __Options_MyStateSample): void { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(((instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - })); - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[AFTER MEMO SCRIPT] script: -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -import { Component as Component, StorageLink as StorageLink, State as State } from "@koalaui.arkts-arkui.Common"; - -import { UserView as UserView, UserViewBuilder as UserViewBuilder } from "@koalaui.arkts-arkui.UserView"; - -import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.StateManagement.runtime"; - -import { memo as memo } from "@ohos.arkui.StateManagement.runtime"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - public static isTrue(): string { - return "aa"; - } - - -} - -class MyStateSample extends StructBase { - public __initializeStruct(__memo_context: __memo_context_type, __memo_id: __memo_id_type, initializers?: __Options_MyStateSample, content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + (168924120)), 2); - const __memo_parameter_initializers = __memo_scope.param(0, initializers), __memo_parameter_content = __memo_scope.param(1, content); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - { - __memo_scope.recache() - return; - } - } - - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} - - public aaa: string = ETSGLOBAL.isTrue(); - - protected _build(__memo_context: __memo_context_type, __memo_id: __memo_id_type, style: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: MyStateSample)=> MyStateSample) | undefined, content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, initializers?: __Options_MyStateSample): void { - const __memo_scope = __memo_context.scope(((__memo_id) + (168198604)), 3); - const __memo_parameter_style = __memo_scope.param(0, style), __memo_parameter_content = __memo_scope.param(1, content), __memo_parameter_initializers = __memo_scope.param(2, initializers); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - Column.instantiateImpl(__memo_context, ((__memo_id) + (229216764)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - const __memo_scope = __memo_context.scope(((__memo_id) + (131080140)), 0); - if (__memo_scope.unchanged) { - __memo_scope.recache(__memo_scope.cached) - return; - } - Text.instantiateImpl(__memo_context, ((__memo_id) + (122349231)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello World!") - Text.instantiateImpl(__memo_context, ((__memo_id) + (259830593)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), (this).aaa) - Button.instantiateImpl(__memo_context, ((__memo_id) + (23671947)), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "change") - { - __memo_scope.recache() - return; - } - })); - { - __memo_scope.recache() - return; - } - } - - public constructor() {} - -} - -class ComExampleTrivialApplication extends UserView { - public getBuilder(): UserViewBuilder { - let wrapper = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { - MyStateSample.instantiateImpl(__memo_context, ((__memo_id) + (44218244)), undefined, ((): MyStateSample => { - return new MyStateSample(); - }), ({} as __Options_MyStateSample), undefined); - }); - return wrapper; - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - - -[MEMO PLUGIN] AFTER CHECKED EXIT -plugin checked finished -[TS WRAPPER] GET AST FROM CONTEXT -[TS WRAPPER] DESTROY AND RECREATE -[TS WRAPPER] PROCEED TO STATE: 7 -es2panda bin generated -"/home/wuhaibin/newcode/oh/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/bin/ark_link" --output "/home/wuhaibin/newcode/oh/developtools/ace_ets2bundle/arkui-plugins/test/dist/modules_static.abc" -- @"dist/cache/fileInfo.txt" diff --git a/arkui-plugins/test/ut/common/annotation.test.ts b/arkui-plugins/test/ut/common/annotation.test.ts index cf9f7d191eab5ce015343fd51fb46337989f3bdc..663454255f56cdc6f457a0ce86e45b7c01be1860 100644 --- a/arkui-plugins/test/ut/common/annotation.test.ts +++ b/arkui-plugins/test/ut/common/annotation.test.ts @@ -13,24 +13,210 @@ * limitations under the License. */ -import { PluginTestContext, PluginTester } from '../../utils/plugin-tester'; -import { annotation } from '../../../common/arkts-utils'; import * as arkts from '@koalaui/libarkts'; +import path from 'path'; +import { PluginTester } from '../../utils/plugin-tester'; +import { BuildConfig, PluginTestContext } from '../../utils/shared-types'; +import { mockBuildConfig } from '../../utils/artkts-config'; +import { parseDumpSrc } from '../../utils/parse-string'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../utils/path-config'; +import { annotation } from '../../../common/arkts-utils'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { AbstractVisitor } from '../../../common/abstract-visitor'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; + +const COMMON_UTILS_DIR_PATH: string = 'common-utils'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMMON_UTILS_DIR_PATH, 'annotation.ets')]; + +const pluginTester = new PluginTester('test arkts-utils', buildConfig); -const pluginTester = new PluginTester('test arkts-utils'); +type AnnotationAstNode = + | arkts.ClassDefinition + | arkts.ClassProperty + | arkts.ETSParameterExpression + | arkts.ArrowFunctionExpression + | arkts.MethodDefinition + | arkts.VariableDeclaration + | arkts.TSInterfaceDeclaration + | arkts.TSTypeAliasDeclaration; + +class AnnotationVisitor extends AbstractVisitor { + isRemover: boolean; + + constructor(isRemover?: boolean) { + super(); + this.isRemover = !!isRemover; + } + + private testAnnotation(): arkts.AnnotationUsage { + return annotation('TestAnno'); + } + + addTestAnnotation(node: AnnotationAstNode): void { + if (arkts.isEtsParameterExpression(node)) { + node.annotations = [this.testAnnotation()]; + } else if (arkts.isMethodDefinition(node)) { + node.scriptFunction.setAnnotations([this.testAnnotation()]); + node.setOverloads( + node.overloads.map((ov) => { + if (this.isAnnotationNode(ov)) { + this.addTestAnnotation(ov); + } + return ov; + }) + ); + } else { + node.setAnnotations([this.testAnnotation()]); + } + } + + removeTestAnnotation(node: AnnotationAstNode): void { + if (arkts.isEtsParameterExpression(node)) { + node.annotations = []; + } else if (arkts.isMethodDefinition(node)) { + node.scriptFunction.setAnnotations([]); + node.setOverloads( + node.overloads.map((ov) => { + if (this.isAnnotationNode(ov)) { + this.removeTestAnnotation(ov); + } + return ov; + }) + ); + } else { + node.setAnnotations([]); + } + } + + isAnnotationNode(node: arkts.AstNode): node is AnnotationAstNode { + return ( + arkts.isClassDefinition(node) || + arkts.isClassProperty(node) || + arkts.isMethodDefinition(node) || + arkts.isEtsParameterExpression(node) || + arkts.isArrowFunctionExpression(node) || + arkts.isMethodDefinition(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ); + } + + visitor(node: arkts.AstNode): arkts.AstNode { + if (this.isAnnotationNode(node)) { + if (this.isRemover) { + this.removeTestAnnotation(node); + } else { + this.addTestAnnotation(node); + } + } + return this.visitEachChild(node); + } +} + +function addAnnotationTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + const annotationAdder = new AnnotationVisitor(); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; +} + +function removeAnnotationTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + const annotationAdder = new AnnotationVisitor(true); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; +} + +const addAnnotation: Plugins = { + name: 'add-annotation', + parsed: addAnnotationTransform, + checked: addAnnotationTransform, +}; + +const removeAnnotation: Plugins = { + name: 'remove-annotation', + parsed: removeAnnotationTransform, + checked: removeAnnotationTransform, +}; + +const expectedParseSnapshot: string = ` +@Retention({policy:\"SOURCE\"}) @interface TestAnno {} +@TestAnno() type TestType = number; +@TestAnno() (() => {}) +@TestAnno() class A { + @TestAnno() public prop: number = 1; + @TestAnno() public method(@TestAnno() arg1: number): void { + @TestAnno() const a: number = arg1; + } + @TestAnno() public constructor() {} +} +@TestAnno() interface __A { + @TestAnno() prop: number; +} +`; + +function testParseAnnotation(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParseSnapshot)); +} + +const expectedCheckSnapshot: string = ` +@TestAnno() function main() {} +@TestAnno() (() => {}); +@Retention({policy:\"SOURCE\"}) @interface TestAnno {} +@TestAnno() type TestType = number; +@TestAnno() class A { + @TestAnno() public prop: number = 1; + @TestAnno() public method(@TestAnno() arg1: number): void { + @TestAnno() const a: number = arg1; + } + @TestAnno() public constructor() {} +} +@TestAnno() interface __A { + @TestAnno() set prop(prop: number) + @TestAnno() get prop(): number +} +`; -function testAnnotation(this: PluginTestContext): void { - const anno: arkts.AnnotationUsage = annotation('State'); - expect(arkts.isAnnotationUsage(anno)).toBeTruthy(); - expect(anno.dumpSrc()).toBe('@State() '); +function testCheckAnnotation(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckSnapshot)); } pluginTester.run( 'annotation', - [], + [addAnnotation, removeAnnotation], { - parsed: [testAnnotation], - checked: [testAnnotation], + 'parsed:add-annotation': [testParseAnnotation], + 'checked:add-annotation': [testCheckAnnotation], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts index a3d7d2909665884324dfb5ca82cacde1f7be36f9..1506b9ca1a38bdce1d4197f24956746f45c044be 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -28,9 +29,10 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUN const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@functions.OptionalParametersAnnotation({minArgCount:3}) function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 5); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_arg3 = __memo_scope.param(2, arg3), __memo_parameter_arg4 = __memo_scope.param(3, arg4), __memo_parameter_arg5 = __memo_scope.param(4, arg5); if (__memo_scope.unchanged) { @@ -48,7 +50,7 @@ function main() {} return; } } -@functions.OptionalParametersAnnotation({minArgCount:1}) function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg4 = __memo_scope.param(1, arg4), __memo_parameter_arg5 = __memo_scope.param(2, arg5); if (__memo_scope.unchanged) { @@ -68,9 +70,9 @@ function main() {} return; } } -@functions.OptionalParametersAnnotation({minArgCount:0}) function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { +@memo() function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { let arg1: int = (((gensym%%_) !== (undefined)) ? gensym%%_ : (10 as int)); - let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): int => { + let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -99,7 +101,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in functions', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba06b5787d39a952bd0dfaa26076b8baf031861d --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts @@ -0,0 +1,155 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'complex-memo-intrinsic.ets'), +]; + +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test complex memo intrinsic function', buildConfig, projectConfig); + +const expectedScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +function main() {} + + +@memo_intrinsic() export function factory(__memo_context: __memo_context_type, __memo_id: __memo_id_type, compute: (()=> Value)): Value + +export function cb(callback?: (()=> void)) { + if (callback) { + return; + } +} + +@memo_intrinsic() export function impl(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() style: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA)=> void) | undefined), arr: SimpleArray, gensym%%_1?: string): void { + let err: string = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : ("error message" as string)); + const s = factory(__memo_context, ((__memo_id) + (90010973)), (() => { + return new A(); + })); + s.aaa(arr.length); + ({let gensym%%_68 = style; + (((gensym%%_68) == (null)) ? undefined : gensym%%_68(__memo_context, ((__memo_id) + (222201816)), s))}); + if (!(s.bbb.get("some_key"))) { + throw new Error(err); + } + if (s.ccc) { + cb((() => { + return s.ddd.forEach(((s: number, t: string) => { + console.log(err); + return; + })); + })); + } else { + return; + } +} + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} + +interface IA { + set ccc(ccc: boolean) + + get ccc(): boolean + +} + +class A implements IA { + public bbb: Map = new Map(); + + public ddd: Map = new Map(); + + public aaa(value: number): void {} + + public constructor() {} + + private _$property$_ccc: boolean = false; + + set ccc(_$property$_ccc: boolean) { + this._$property$_ccc = _$property$_ccc; + return; + } + + public get ccc(): boolean { + return this._$property$_ccc; + } + +} + +export type SimpleArray = (Array | ReadonlyArray | Readonly>); + +class Use { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (228150357)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + const style = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (237001330)), 1); + const __memo_parameter_attributes = __memo_scope.param(0, attributes); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }); + const arr = [1, 2, 3, 4]; + impl(__memo_context, ((__memo_id) + (158199735)), style, arr); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform complex @memo_intrinsic calls in functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts index db15946ae06df9c3a9bf9d0d1037a027fae86831..99fba4a09dabbb50bf34e9e9232a20a0090d82cb 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,11 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -43,22 +46,52 @@ function main() {} __memo_scope.recache(); return; } -}); -@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +} +@memo() function funcWithMemoBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() memo_arg: MemoBuilder): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_memo_arg = __memo_scope.param(0, memo_arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +function funcWithArg(arg: (()=> void)): void {} +function func(): void {} +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } funcA(__memo_context, ((__memo_id) + ())); + funcWithMemoBuilder(__memo_context, ((__memo_id) + ()), { + builder: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + funcWithArg((() => { + func(); + return; + })); + { + __memo_scope.recache(); + return; + } + }), + }); { __memo_scope.recache(); return; } -} +}); class A { - public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -72,6 +105,10 @@ class A { } public constructor() {} } +interface MemoBuilder { + @memo() set builder(builder: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) + @memo() get builder(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) +} `; function testMemoTransformer(this: PluginTestContext): void { @@ -80,7 +117,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare functions and calls', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts index 8e549464318354654d0303cbc1adda98572cd928..a73b950b6e13f509ac97580facb00a16306e1f18 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -43,13 +45,13 @@ function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo return; } } -function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -68,14 +70,14 @@ function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo } } class A { - public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { - __memo_scope.cached; - return; + __memo_scope.cached; + return; } let func = (() => {}); - let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner functions', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..02fc30648d42839da531deb3fa2280e52eba2436 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts @@ -0,0 +1,186 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'internal-memo-arg.ets'), +]; + +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test internal memo argument calls', buildConfig, projectConfig); + +const expectedScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { IncrementalNode as IncrementalNode } from "@koalaui.runtime.tree.IncrementalNode"; + +import { ControlledScope as ControlledScope, StateManager as StateManager } from "@koalaui.runtime.states.State"; + +function main() {} + + +export function __context(): __memo_context_type + +export function __id(): __memo_id_type + +@memo_entry() export function memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { + return entry(__memo_context, ((__memo_id) + (75311131))); +} + +@memo_entry() export function memoEntry1(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T)=> R), arg: T): R { + return entry(__memo_context, ((__memo_id) + (168506859)), arg); +} + +@memo_entry() export function memoEntry2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: T1, arg2: T2)=> R), arg1: T1, arg2: T2): R { + return entry(__memo_context, ((__memo_id) + (76962895)), arg1, arg2); +} + +@memo_intrinsic() export function contextLocalValue(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string): Value { + return __memo_context.valueBy(name); +} + +@memo_intrinsic() export function contextLocalScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string, value: Value, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + const scope = __memo_context.scope(__memo_id, 1); + scope.param(0, value, undefined, name, true); + if (scope.unchanged) { + scope.cached; + } else { + content(__memo_context, ((__memo_id) + (2633070))); + scope.recache(); + } +} + +@memo_intrinsic() export function NodeAttach(__memo_context: __memo_context_type, __memo_id: __memo_id_type, create: (()=> Node), @memo() update: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, node: Node)=> void), reuseKey?: string): void { + const scope = __memo_context.scope(__memo_id, 0, create, undefined, undefined, undefined, reuseKey); + if (scope.unchanged) { + scope.cached; + } else { + try { + if (!reuseKey) { + update(__memo_context, ((__memo_id) + (6025780)), (__memo_context.node as Node)); + } else { + memoEntry(__memo_context, 0, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (31840240)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + update(__memo_context, ((__memo_id) + (253864074)), (__memo_context.node as Node)); + { + __memo_scope.recache(); + return; + } + })); + } + } finally { + scope.recache(); + } + } +} + +@memo_intrinsic() export function rememberControlledScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, invalidate: (()=> void)): ControlledScope { + return __memo_context.controlledScope(__memo_id, invalidate); +} + +@memo() export function Repeat(__memo_context: __memo_context_type, __memo_id: __memo_id_type, count: int, @memo() action: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, index: int)=> void)) { + const __memo_scope = __memo_context.scope(((__memo_id) + (200707415)), 2); + const __memo_parameter_count = __memo_scope.param(0, count), __memo_parameter_action = __memo_scope.param(1, action); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + for (let i = 0;((i) < (__memo_parameter_count.value));(i++)) { + memoEntry1(__memo_context, i, __memo_parameter_action.value, i); + } + { + __memo_scope.recache(); + return; + } +} + + +@Retention({policy:"SOURCE"}) @interface memo_intrinsic {} + +@Retention({policy:"SOURCE"}) @interface memo_entry {} + +export class MemoCallbackContext { + private readonly context: __memo_context_type; + + private readonly id: __memo_id_type; + + private constructor(context: __memo_context_type, id: __memo_id_type) { + this.context = context; + this.id = id; + } + + @memo() public static Make(__memo_context: __memo_context_type, __memo_id: __memo_id_type): MemoCallbackContext { + const __memo_scope = __memo_context.scope(((__memo_id) + (41727473)), 0); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + return __memo_scope.recache(new MemoCallbackContext(__memo_context, __memo_id)); + } + +} + +export class CustomComponent { + @memo() public static _instantiateImpl(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + (214802466)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + const context: StateManager = (__memo_context as StateManager); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform internal __context() and __id() calls in functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts index 19b061ad6bc38ec118d84e479fc4e5404308b882..c59453f5f38a497aedc52bc68b4102fc6d85c33f 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,30 +31,31 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { +@memo() function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(1); } -function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { +@memo() function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(\"1\"); } -function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { +@memo() function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(false); } -function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { +@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -62,21 +64,21 @@ function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): str: \"1\", }); } -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(((str: string) => {})); } -function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { +@memo() function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(new C(\"1\")); } -function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { +@memo() function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { const __memo_scope = __memo_context.scope<(()=> void)>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with non-void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts index 7db7cd676d57630897af74cb9328c96c2d64e75d..eca62211fc62871f30dcbfc225feb97dc0c82677 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,15 +31,27 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() function A(__memo_context: __memo_context_type, __memo_id: __memo_id_type): Attribute -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { +@memo() export function A(__memo_context: __memo_context_type, __memo_id: __memo_id_type): Attribute +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { const __memo_scope = __memo_context.scope>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } - return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => {})); + return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); } @memo() type ItemBuilder = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item)=> void); interface Item { @@ -49,13 +62,13 @@ interface Attribute { @memo() each(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() itemGenerator: ItemBuilder): Attribute } class B { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item): void => { + A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_ri = __memo_scope.param(0, ri); if (__memo_scope.unchanged) { @@ -82,7 +95,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with type reference', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts index 08c0eb3bdde9bba0673ab6a0c22cf7af771eda8c..fd2f3cb8885fe73b2a58e68377b065d63ca14a04 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo function', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,7 +53,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts index f23a8d2529875eded121b1b681d55546030393d1..18b3a26e3c2fdeeca0190011d9eb903b497f1a4b 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -28,14 +29,25 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAM const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} ((arg: (()=> void)) => {})((() => {})); -((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() (() => {})); +((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +})); ((gensym%%_1?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (201676739)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -46,7 +58,7 @@ function main() {} return; } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); -})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (209782503)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -58,14 +70,14 @@ function main() {} } })); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -86,7 +98,7 @@ function main() {} __memo_scope.recache(); return; } - })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -103,13 +115,13 @@ function main() {} } }); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string): void => { + let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string) => { let name: string = (((gensym%%_) !== (undefined)) ? gensym%%_ : (\"old\" as string)); const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_name = __memo_scope.param(0, name); @@ -131,7 +143,7 @@ function main() {} (() => { let foo = ((gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -143,7 +155,7 @@ function main() {} } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); }); - foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -163,7 +175,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in lambdas', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts index be488294a6ca913309fa32ba707384b588cf1857..60d5c5985a778d9007d1fda091eeccd099ccc8d8 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { Plugins } from '../../../../common/plugin-context'; import { uiTransform } from '../../../../ui-plugins'; @@ -31,19 +32,18 @@ buildConfig.compileFiles = [ const parsedTransform: Plugins = { name: 'state-complex-type', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; -import { memo as memo } from "@ohos.arkui.stateManagement"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; function main() {} -function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { +@memo() function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { const __memo_scope = __memo_context.scope(((__memo_id) + (38567515)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -57,7 +57,7 @@ function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } } -function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { +@memo() function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { const __memo_scope = __memo_context.scope(((__memo_id) + (167482260)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -68,7 +68,7 @@ function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } class B { - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + (146437675)), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -87,7 +87,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas about function with receiver feature', - [parsedTransform, memoNoRecheck], + [parsedTransform, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts index f023fef13b9f4a87a60269817fc05e53bd411580..47a14d9545c946b8be810eef71b7d3628db00f68 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,9 +31,35 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_f = __memo_scope.param(0, f); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +function par(f?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} +@memo() function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_f = __memo_scope.param(0, f); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -42,7 +69,7 @@ function main() {} a.foo(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -54,7 +81,7 @@ function main() {} return; } })); - a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -69,7 +96,7 @@ function main() {} bar(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -81,7 +108,7 @@ function main() {} return; } })); - kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -98,33 +125,8 @@ function main() {} return; } }); -@functions.OptionalParametersAnnotation({minArgCount:0}) function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); - const __memo_parameter_f = __memo_scope.param(0, f); - if (__memo_scope.unchanged) { - __memo_scope.cached; - return; - } - { - __memo_scope.recache(); - return; - } -} -@functions.OptionalParametersAnnotation({minArgCount:0}) function par(f?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -@functions.OptionalParametersAnnotation({minArgCount:0}) function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); - const __memo_parameter_f = __memo_scope.param(0, f); - if (__memo_scope.unchanged) { - __memo_scope.cached; - return; - } - { - __memo_scope.recache(); - return; - } -} class A { - @functions.OptionalParametersAnnotation({minArgCount:0}) public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -136,8 +138,8 @@ class A { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public goo(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} - @functions.OptionalParametersAnnotation({minArgCount:0}) public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + public goo(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + @memo() public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -159,7 +161,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform trailing lambdas', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts index 59b48a02458e04db484aca46c70396f2fca00a2d..37b1c13655ec89a611c7d49d6fc1f13b28810d15 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,7 +31,8 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); @@ -63,7 +65,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts index 4f6e3fa92a6c7d7c4056ab836055e662fa0aa522..1cb3c4d175421f97fe2b9d20033a35c988f2ab8d 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -30,16 +31,58 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; + function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + +@memo() function fullName(this: Person, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function foo(this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_a = __memo_scope.param(0, a), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let x = new Person(); - fullName(x, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + fullName(x, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,12 +94,9 @@ function main() {} } })); let f1: F1 = foo; - f1 = goo; let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -67,7 +107,7 @@ function main() {} return; } })); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -78,7 +118,7 @@ function main() {} return; } })); - f2(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f2(__memo_context, ((__memo_id) + ()), a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -94,19 +134,17 @@ function main() {} return; } }); -@functions.OptionalParametersAnnotation({minArgCount:1}) function fullName(this: Person, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - return; -} -@functions.OptionalParametersAnnotation({minArgCount:1}) function foo(this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -@functions.OptionalParametersAnnotation({minArgCount:1}) function goo(a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + class Person { public constructor() {} } + class A { public constructor() {} } -type F1 = ((this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); -type F2 = ((a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); + +@memo() type F1 = ((this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); +@memo() type F2 = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); `; function testMemoTransformer(this: PluginTestContext): void { @@ -115,7 +153,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with receiver', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts index de72519e74bbc1669122263ac6f136e10b4ce9fa..f56a4c7cf0253c6502ce49436f73e8021dcd995b 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -28,10 +29,11 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, MET const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class Test { - public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -43,7 +45,7 @@ class Test { return; } } - public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)): void { + @memo() public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -55,7 +57,7 @@ class Test { return; } } - public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_content = __memo_scope.param(0, content); if (__memo_scope.unchanged) { @@ -68,7 +70,7 @@ class Test { return; } } - public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, arg2: (()=> void) | undefined, content: (()=> void) | undefined): void { + @memo() public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined), arg2: ((()=> void) | undefined), content: ((()=> void) | undefined)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_content = __memo_scope.param(2, content); if (__memo_scope.unchanged) { @@ -83,7 +85,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -109,7 +111,7 @@ class Use { } return __memo_scope.recache(__memo_parameter_value.value); })); - test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -135,7 +137,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in methods', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts index 7657e1dd0ce878e17e8df46d82fec626e2754e57..c611a8e99dcd1ac328a65bc6a48a66103cc53a5a 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -28,16 +29,17 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, MET const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } A.$_invoke(__memo_context, ((__memo_id) + ())); - B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,7 +50,7 @@ function main() {} return; } })); - let x: C | D = C.$_instantiate(__memo_context, ((__memo_id) + ()), (() => { + let x: (C | D) = C.$_instantiate(__memo_context, ((__memo_id) + ()), (() => { return new C(); })); x = D.$_instantiate((() => { @@ -60,7 +62,7 @@ function main() {} } }); class A { - public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -74,11 +76,11 @@ class A { public constructor() {} } class B { - @functions.OptionalParametersAnnotation({minArgCount:0}) public static $_invoke(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + public static $_invoke(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} public constructor() {} } class C { - public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { + @memo() public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_factory = __memo_scope.param(0, factory); if (__memo_scope.unchanged) { @@ -89,7 +91,7 @@ class C { public constructor() {} } class D { - @functions.OptionalParametersAnnotation({minArgCount:1}) public static $_instantiate(factory: (()=> D), @memo() content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): D { + public static $_instantiate(factory: (()=> D), @memo() content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): D { return factory(); } public constructor() {} @@ -102,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform callable class', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts index 0a669b4c085c2bf7abfc2c0cbd8e52c6bbaaf7b1..d1e6012ea7cdde4b5b9293f6336e1a4abfd535f4 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -30,9 +31,10 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,11 +50,11 @@ function main() {} }); declare abstract class A { @memo() public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void - public test_signature(@memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void), @memo() arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined | ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) | undefined, @memo() x: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, y: ((z: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void))=> void)): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) + public test_signature(@memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void), @memo() arg2: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined), @memo() arg3: ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) | undefined)), @memo() x: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, y: ((z: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void))=> void)): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) public constructor() {} } class AA extends A { - public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -73,7 +75,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare methods and calls', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts index 11dae0f7ed8a30c405812e53110d00d47ec5dd09..caf2b1b9fc3addf9a3e03f33f6be9cb5b60fe728 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts @@ -14,27 +14,33 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'internal-calls.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; function main() {} -function __context(): __memo_context_type -function __id(): __memo_id_type +export function __context(): __memo_context_type +export function __id(): __memo_id_type type MemoType = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -45,7 +51,7 @@ class Test { return; } } - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -57,21 +63,21 @@ class Test { return; } } - public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - __context(); - __id(); + __memo_context; + __memo_id; { __memo_scope.recache(); return; } } public memo_lambda() { - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -83,7 +89,7 @@ class Test { } }); } - public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -118,7 +124,7 @@ class Test { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { + @memo() public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { let arg1: int = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (10 as int)); let arg2: (()=> int) = (((gensym%%_2) !== (undefined)) ? gensym%%_2 : ((() => { return 20; @@ -137,7 +143,7 @@ class Test { return; } } - @functions.OptionalParametersAnnotation({minArgCount:0}) public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)): void { + @memo() public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2); if (__memo_scope.unchanged) { @@ -153,7 +159,7 @@ class Test { return; } } - public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType): void { + @memo() public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -176,7 +182,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner calls in methods', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts index a8ddef791a1367d4cb3575135fcfa52205e29514..8957ca871ad3e5e0da09ea031a439130286396e6 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts @@ -14,28 +14,35 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'non-void-method.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; function main() {} -function __context(): __memo_context_type -function __id(): __memo_id_type +export function __context(): __memo_context_type +export function __id(): __memo_id_type @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -46,7 +53,7 @@ class Test { return; } } - public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { + @memo() public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -54,7 +61,7 @@ class Test { } return __memo_scope.recache(__memo_parameter_arg.value); } - public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { + @memo() public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -62,19 +69,19 @@ class Test { } return __memo_scope.recache(__memo_parameter_arg.value); } - public intrinsic_method(): int { + @memo_intrinsic() public intrinsic_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): int { return 0; } - public intrinsic_method_with_this(): int { + @memo_intrinsic() public intrinsic_method_with_this(__memo_context: __memo_context_type, __memo_id: __memo_id_type): int { this.void_method(__memo_context, ((__memo_id) + ())); return 0; } - public memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { + @memo_entry() public memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() entry: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> R)): R { const getContext = (() => { - return __context(); + return __memo_context; }); const getId = (() => { - return __id(); + return __memo_id; }); { const __memo_context = getContext(); @@ -82,10 +89,20 @@ class Test { return entry(__memo_context, ((__memo_id) + ())); } } + @memo() public memo_skip_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, @memo_skip() arg2: string, @memo_skip() arg3: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): string { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_arg1 = __memo_scope.param(0, arg1); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + let a = __memo_parameter_arg1.value; + arg3(__memo_context, ((__memo_id) + ())); + return __memo_scope.recache(arg2); + } public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -109,7 +126,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with non-void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts index 953742b8a28ebc4248b410680ccd4f4971dd75cb..41e73d5bb6563d50dc70a623b274578e99c570a5 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -30,7 +31,8 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo method', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class A { public x: int; @@ -38,7 +40,7 @@ class A { public constructor() {} } class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -49,7 +51,7 @@ class Test { return; } } - public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,7 +62,7 @@ class Test { return; } } - public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -72,7 +74,7 @@ class Test { return; } } - public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -84,7 +86,7 @@ class Test { return; } } - public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { + @memo() public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -96,7 +98,7 @@ class Test { return; } } - public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A): void { + @memo() public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -111,7 +113,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -141,7 +143,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with void return type', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts index 28671e70099f78415a094cd6548f1f8b48adfdb6..b3be707bacd797ed55de8322d0854c2c26cf204d 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -30,16 +31,17 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let a = new AA({ - a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -57,19 +59,19 @@ function main() {} } }); interface A { - @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get a(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) } class AA { - @memo() public a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined; + @memo() public a: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); constructor() { this(undefined); } - public constructor(arg: A | undefined) { + public constructor(arg: (A | undefined)) { this.a = ({let gensym%%_ = arg; (((gensym%%_) == (null)) ? undefined : gensym%%_.a)}); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -91,7 +93,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class constructor', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts index c987200392947b7ce38ffd1d31dd1d034326a613..1a97511af702bcfdf1e613dbc950f2081d37d235 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -30,13 +31,14 @@ buildConfig.compileFiles = [ const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class A { public arg: (()=> void); @memo() public memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); - @memo() public memo_optional_arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined; - @memo() public memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() public memo_optional_arg?: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); + @memo() public memo_union_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -50,7 +52,7 @@ class A { public arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); public constructor() { this.arg = (() => {}); - this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -61,7 +63,7 @@ class A { return; } }); - this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -73,7 +75,7 @@ class A { } }); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -96,7 +98,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts index 99053d0efc1ff8c0704aae5ef3e8a5bb7f94ba85..a28664fde2474320eb0656d51a88c3ef73b1cb8c 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -28,9 +29,10 @@ buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, PRO const pluginTester = new PluginTester('test memo property', buildConfig); const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -38,7 +40,7 @@ function main() {} } let a: A = { arg: (() => {}), - memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -49,7 +51,7 @@ function main() {} return; } }), - memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,7 +62,7 @@ function main() {} return; } }), - arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,12 +82,12 @@ function main() {} interface A { set arg(arg: (()=> void)) get arg(): (()=> void) - @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get memo_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) - @memo() set memo_optional_arg(memo_optional_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void - @memo() get memo_optional_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined - @memo() set memo_union_arg(memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void - @memo() get memo_union_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined + @memo() set memo_optional_arg(memo_optional_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + @memo() get memo_optional_arg(): (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + @memo() set memo_union_arg(memo_union_arg: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + @memo() get memo_union_arg(): (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) set arg_memo_type(arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) get arg_memo_type(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) } @@ -97,7 +99,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform interface properties', - [memoNoRecheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts similarity index 35% rename from arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts rename to arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts index fc5ee09c61f539d5e2bd36eda2dff9a5221425c3..f921f5f4ac9b3d87d369245aeb0a41e18d557017 100644 --- a/arkui-plugins/test/ut/ui-plugins/xcomponent/xcomponent-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts @@ -1,3 +1,4 @@ + /* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,82 +15,81 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; -const XCOMPONENT_DIR_PATH: string = 'xcomponent'; +const ANIMATION_DIR_PATH: string = 'animation'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, XCOMPONENT_DIR_PATH, 'xcomponent-basic.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, ANIMATION_DIR_PATH, 'animatable-extend-basic.ets'), ]; -const xcomponentTransform: Plugins = { - name: 'xcomponent', +const animatableExtendTransform: Plugins = { + name: 'animatable-extend', parsed: uiTransform().parsed, -} +}; -const pluginTester = new PluginTester('test basic XComponent transform', buildConfig); +const pluginTester = new PluginTester('test basic animatableExtend transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIXComponentAttribute as UIXComponentAttribute } from "@ohos.arkui.component"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UIFlexAttribute as UIFlexAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, Flex as Flex, XComponent as XComponent, FlexDirection as FlexDirection, XComponentType as XComponentType, Entry as Entry, XComponentController as XComponentController, ItemAlign as ItemAlign, FlexAlign as FlexAlign, XComponentParameter as XComponentParameter } from "@ohos.arkui.component"; +import { Text as Text, Column as Column, Component as Component, Color as Color, Curve as Curve } from "@ohos.arkui.component"; -function main() {} +import { Entry as Entry } from "@ohos.arkui.component"; +function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../animation/animatable-extend-basic", + pageFullPath: "test/demo/mock/animation/animatable-extend-basic", + integratedHsp: "false", + } as NavInterface)); -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_myXComponentController = ((({let gensym___221905990 = initializers; - (((gensym___221905990) == (null)) ? undefined : gensym___221905990.myXComponentController)})) ?? (new XComponentController())); - } +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct AnimatablePropertyExample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_AnimatablePropertyExample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_AnimatablePropertyExample | undefined)): void {} - private __backing_myXComponentController?: XComponentController; - - public get myXComponentController(): XComponentController { - return (this.__backing_myXComponentController as XComponentController); - } - - public set myXComponentController(value: XComponentController) { - this.__backing_myXComponentController = value; - } - - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Flex(@memo() ((instance: UIFlexAttribute): void => { - instance.width("100%").height("100%"); - return; - }), { - direction: FlexDirection.Column, - alignItems: ItemAlign.Center, - justifyContent: FlexAlign.Start, - }, (() => { - XComponent(undefined, ({ - id: "xComponentId", - type: XComponentType.TEXTURE, - libraryname: "nativerender", - controller: this.myXComponentController, - } as XComponentParameter), "", undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.animationStart({ + duration: 2000, + curve: Curve.Ease, + }).backgroundColor(Color.Red).animationStop({ + duration: 2000, + curve: Curve.Ease, + }).animationStart({ + duration: 2000, + curve: Curve.Ease, + }).fontSize(20).animationStop({ + duration: 2000, + curve: Curve.Ease, + }).width("100%"); + return; + }), "AnimatableProperty", undefined, undefined); })); } @@ -97,17 +97,14 @@ function main() {} } -interface __Options_Index { - set myXComponentController(myXComponentController: XComponentController | undefined) - - get myXComponentController(): XComponentController | undefined +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { } class __EntryWrapper extends EntryPoint { @memo() public entry(): void { - Index._instantiateImpl(undefined, (() => { - return new Index(); + AnimatablePropertyExample._instantiateImpl(undefined, (() => { + return new AnimatablePropertyExample(); }), undefined, undefined, undefined); } @@ -116,17 +113,23 @@ class __EntryWrapper extends EntryPoint { } `; -function testXComponentTransformer(this: PluginTestContext): void { +const expectedHeader = + ` + __createOrSetAnimatableProperty(functionName: string, value: number | AnimatableArithmetic, callback: ((value: number | AnimatableArithmetic)=> void)): void + `; + +function testAnimatableExtendTransformer(this: PluginTestContext): void { expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); } pluginTester.run( - 'test basic XComponent transform', - [xcomponentTransform, uiNoRecheck], + 'test basic animation transform', + [animatableExtendTransform, uiNoRecheck, recheck], { - checked: [testXComponentTransformer], + checked: [testAnimatableExtendTransformer], }, { stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.common'] }, } ); diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts index dd4678db61622b3ddf437dbce6fec4d19a40b103..51d4f1b7641c68841dbf3c2750393f69208705f6 100644 --- a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; @@ -32,23 +33,23 @@ buildConfig.compileFiles = [ const animationTransform: Plugins = { name: 'animation', parsed: uiTransform().parsed, -} +}; const pluginTester = new PluginTester('test basic animation transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Text as Text, Column as Column, Component as Component, Color as Color, Curve as Curve } from "@ohos.arkui.component"; @@ -57,16 +58,22 @@ import { Entry as Entry } from "@ohos.arkui.component"; function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../animation/animation-basic", + pageFullPath: "test/demo/mock/animation/animation-basic", + integratedHsp: "false", + } as NavInterface)); - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class AnimatablePropertyExample extends CustomComponent { - public __initializeStruct(initializers: __Options_AnimatablePropertyExample | undefined, @memo() content: (()=> void) | undefined): void {} +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct AnimatablePropertyExample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_AnimatablePropertyExample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_AnimatablePropertyExample | undefined): void {} + public __updateStruct(initializers: (__Options_AnimatablePropertyExample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: AnimatablePropertyExample)=> AnimatablePropertyExample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_AnimatablePropertyExample | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.animationStart({ duration: 2000, curve: Curve.Ease, @@ -89,7 +96,7 @@ function main() {} } -interface __Options_AnimatablePropertyExample { +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { } @@ -105,17 +112,24 @@ class __EntryWrapper extends EntryPoint { } `; +const expectedHeader = + ` + animationStart(value: AnimateParam | undefined): this + animationStop(value: AnimateParam | undefined): this + `; + function testAnimationTransformer(this: PluginTestContext): void { expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); } pluginTester.run( 'test basic animation transform', - [animationTransform, uiNoRecheck], + [animationTransform, uiNoRecheck, recheck], { checked: [testAnimationTransformer], }, { stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.common'] }, } ); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..b38eeb4a638446262bd99177d5bb1f613e2ffdf7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/block-in-switch-case.test.ts @@ -0,0 +1,505 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'block-in-switch-case.ets'), +]; + +const pluginTester = new PluginTester('test block statement in switch case', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { TextImpl as TextImpl } from "arkui.component.text"; +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Text as Text, Column as Column, Component as Component } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (2)); + } + + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + + private __backing_num?: int; + + public get num(): int { + return (this.__backing_num as int); + } + + public set num(value: int) { + this.__backing_num = value; + } + + @memo() public build() { + ColumnImpl(@memo() ((instance: ColumnAttribute): void => { + instance.setColumnOptions(undefined).applyAttributesFinish(); + return; + }), @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case 0: { + break; + } + case 1: { + ConditionBranch(@memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } + case 2: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + } + case 3: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + case 4: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + return; + } + })); + break; + } + case 5: { + ConditionBranch(@memo() (() => { + { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + } + case 6: { + ConditionBranch(@memo() (() => { + { + return; + } + })); + break; + } + case 7: { + ConditionBranch(@memo() (() => { + { + return; + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("111", undefined).applyAttributesFinish(); + return; + }), undefined); + } + })); + break; + } + default: { + break; + } + } + })); + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("hello world", undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_SwitchCase { + set num(num: (int | undefined)) + + get num(): (int | undefined) + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { TextImpl as TextImpl } from "arkui.component.text"; +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Text as Text, Column as Column, Component as Component } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (2)); + } + + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + + private __backing_num?: int; + + public get num(): int { + return (this.__backing_num as int); + } + + public set num(value: int) { + this.__backing_num = value; + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (261239291)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ColumnImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: ColumnAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setColumnOptions(undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (147868395)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + (186113)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (186336799)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case 0: { + break; + } + case 1: { + ConditionBranch(__memo_context, ((__memo_id) + (27357263)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (78642435)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + { + __memo_scope.recache(); + return; + } + })); + } + case 2: { + ConditionBranch(__memo_context, ((__memo_id) + (220977109)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (91459184)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + } + case 3: { + ConditionBranch(__memo_context, ((__memo_id) + (143235624)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (214575380)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + case 4: { + ConditionBranch(__memo_context, ((__memo_id) + (7513933)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (235688754)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + return; + } + })); + break; + } + case 5: { + ConditionBranch(__memo_context, ((__memo_id) + (58475451)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (257250368)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + } + case 6: { + ConditionBranch(__memo_context, ((__memo_id) + (60292460)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (21099142)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + return; + } + })); + break; + } + case 7: { + ConditionBranch(__memo_context, ((__memo_id) + (34940192)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (15961624)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + return; + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("111", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + } + { + __memo_scope.recache(); + return; + } + })); + break; + } + default: { + break; + } + } + { + __memo_scope.recache(); + return; + } + })); + TextImpl(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.setTextOptions("hello world", undefined).applyAttributesFinish(); + { + __memo_scope.recache(); + return; + } + }), undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_SwitchCase { + set num(num: (int | undefined)) + + get num(): (int | undefined) + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test block statement in switch case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3465c1c59a73fcc23bd5218792954930fb98e607 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/else-if-in-content.test.ts @@ -0,0 +1,278 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'else-if-in-content.ets'), +]; + +const pluginTester = new PluginTester('test else-if conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'else-if', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct ElseIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_ElseIf | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_ElseIf | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => {})); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf 1\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else 1\", undefined, undefined); + })); + } + } + })); + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => {})); + } else { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf 2\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else 2\", undefined, undefined); + })); + } + })); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_ElseIf { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct ElseIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_ElseIf | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_ElseIf | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_ElseIf { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test else-if condition branch', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d84aeb7853ffc90b4d341c2f7f542d9b0a2407d0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-break-in-nested-content.test.ts @@ -0,0 +1,228 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-break-in-nested-content.ets'), +]; + +const pluginTester = new PluginTester('test if condition branch break statements in nested builder lambda call\'s trailing lambda', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-else', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Column as Column, Text as Text } from \"@ohos.arkui.component\"; +import hilog from \"@ohos.hilog\"; +function main() {} +@Component() final struct A extends CustomComponent { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_A | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + hilog.info(0x0000, \"very inside\", \"1\"); + })); + return; + Text(undefined, \"1\", undefined, undefined); + } + })); + })); + })); + })); + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + hilog.info(0x0000, \"outside column\", \"2\"); + Text(undefined, \"1\", undefined, undefined); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_A { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Column as Column, Text as Text } from \"@ohos.arkui.component\"; +import hilog from \"@ohos.hilog\"; +function main() {} +@Component() final struct A extends CustomComponent { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_A | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + hilog.info(0x0000, \"very inside\", \"1\"); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"1\", undefined, undefined); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + hilog.info(0x0000, \"outside column\", \"2\"); + Text(__memo_context, ((__memo_id) + ()), undefined, \"1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_A { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if condition branch break statements', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..98209c11ae8a68a3777e2faa169cd0386144cea1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-else-in-content.test.ts @@ -0,0 +1,261 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-else-in-content.ets'), +]; + +const pluginTester = new PluginTester('test if-else conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-else', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfElse extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfElse | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_IfElse | undefined)): void {} + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-if\", undefined, undefined); + })); + } else { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-elseIf\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"if-else\", undefined, undefined); + })); + } + } + })); + })); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"elseIf\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"else\", undefined, undefined); + })); + return; + Text(undefined, \"after-return\", undefined, undefined); + } + } + })); + Text(undefined, \"hello world\", undefined, undefined); + })); + } + public constructor() {} +} +@Component() export interface __Options_IfElse { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfElse extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfElse | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_IfElse | undefined)): void {} + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-if\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-elseIf\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"if-else\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"elseIf\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"else\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after-return\", undefined, undefined); + } + } + { + __memo_scope.recache(); + return; + } + })); + Text(__memo_context, ((__memo_id) + ()), undefined, \"hello world\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_IfElse { +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if-else', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..66e215a5bef36835251384610f34520d0375e52f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/if-in-switch-in-content.test.ts @@ -0,0 +1,299 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'if-in-switch-in-content.ets'), +]; + +const pluginTester = new PluginTester('test if conditions in switch-case in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'if-in-switch', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfInSwitch extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfInSwitch | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_IfInSwitch | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"-1\": { + ConditionBranch(@memo() (() => { + { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 1\", undefined, undefined); + })); + } else { + if (false) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 2\", undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 3\", undefined, undefined); + })); + } + } + })); + } + })); + } + case \"2\": { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 4\", undefined, undefined); + })); + } + })); + })); + } + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_IfInSwitch { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct IfInSwitch extends CustomComponent { + public __initializeStruct(initializers: (__Options_IfInSwitch | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_IfInSwitch | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"-1\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + if (false) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } else { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 3\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + } + case \"2\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 4\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_IfInSwitch { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test if conditions in switch-case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..7bfebe64eb48474a8c925462a3d4fbce25ef49a7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/non-builder-within-builder.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'non-builder-within-builder.ets'), +]; + +const pluginTester = new PluginTester( + 'test no conditionScope in non-@Builder function within @Builder function', + buildConfig +); + +const parsedTransform: Plugins = { + name: 'with-no-condition', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Component as Component, Builder as Builder } from \"@ohos.arkui.component\"; +function main() {} +@memo() function TestComponent(init: TestInitCallback, update: TestUpdateCallback): void {} +type TestInitCallback = (()=> void); +type TestUpdateCallback = (()=> void); +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + @memo() public build() { + TestComponent((() => { + if (true) { + } + }), (() => { + if (false) { + } + })); + } + public constructor() {} +} +@Component() export interface __Options_MyStateSample { +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +pluginTester.run( + 'test no conditionScope in non-@Builder function', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f4ac9e902691275846c4543a14157d43945220e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-case-in-content.test.ts @@ -0,0 +1,244 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'switch-case-in-content.ets'), +]; + +const pluginTester = new PluginTester('test switch-case conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'switch-case', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"0\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 0\", undefined, undefined); + })); + } + case \"1\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 1\", undefined, undefined); + })); + break; + Text(undefined, \"after break\", undefined, undefined); + } + case \"2\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 2\", undefined, undefined); + })); + return; + Text(undefined, \"after return\", undefined, undefined); + } + default: { + ConditionBranch(@memo() (() => { + Text(undefined, \"default\", undefined, undefined); + })); + } + } + })); + Text(undefined, \"hello world\", undefined, undefined); + })); + } + public constructor() {} +} +@Component() export interface __Options_SwitchCase { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchCase extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchCase | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchCase | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"0\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 0\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + case \"1\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 1\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + break; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after break\", undefined, undefined); + } + case \"2\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 2\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + return; + Text(__memo_context, ((__memo_id) + ()), undefined, \"after return\", undefined, undefined); + } + default: { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"default\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + Text(__memo_context, ((__memo_id) + ()), undefined, \"hello world\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_SwitchCase { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test switch-case', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..588d255e396f05f1d65c4915417cb4a39ed6069c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/switch-in-if-in-content.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'switch-in-if-in-content.ets'), +]; + +const pluginTester = new PluginTester('test switch-statement in if conditions in builder lambda call', buildConfig); + +const parsedTransform: Plugins = { + name: 'switch-case', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchInIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchInIf | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchInIf | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + ConditionScope(@memo() (() => { + switch (this.num) { + case \"0\": { + ConditionBranch(@memo() (() => { + Text(undefined, \"case 0\", undefined, undefined); + })); + } + } + })); + })); + } + })); + })); + } + public constructor() {} +} +@Component() export interface __Options_SwitchInIf { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component } from \"@ohos.arkui.component\"; +function main() {} +@Component() final struct SwitchInIf extends CustomComponent { + public __initializeStruct(initializers: (__Options_SwitchInIf | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_num = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.num)})) ?? (\"1\")); + } + public __updateStruct(initializers: (__Options_SwitchInIf | undefined)): void {} + private __backing_num?: string; + public get num(): string { + return (this.__backing_num as string); + } + public set num(value: string) { + this.__backing_num = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + switch (this.num) { + case \"0\": { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"case 0\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_SwitchInIf { + set num(num: (string | undefined)) + get num(): (string | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test switch-statement in if', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..570c82a4beff068515ed2a9d92023d629f521632 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/condition-scope/with-builder.test.ts @@ -0,0 +1,498 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda/condition-scope'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'with-builder.ets'), +]; + +const pluginTester = new PluginTester('test conditionScope within @Builder or @BuilderParam', buildConfig); + +const parsedTransform: Plugins = { + name: 'with-builder', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from \"@ohos.arkui.component\"; +const wBuilder = wrapBuilder(ParamBuilder); +function main() {} +@memo() function MyBuilder(): void { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder function\", undefined, undefined); + })); + } + })); +} +@memo() function ParamBuilder(@Builder() @memo() gensym%%_?: (()=> void)): void { + let param: (()=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : ((() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder parameter\", undefined, undefined); + })); + } + })); + }) as (()=> void))); + param(); +} +wBuilder = wrapBuilder(ParamBuilder); +@Component() final struct MyStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStruct | undefined)): void {} + @memo() public myBuilderMethod() { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder method\", undefined, undefined); + })); + } + })); + } + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + wBuilder.builder(@Builder() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"with Builder lambda\", undefined, undefined); + })); + } + })); + })); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + myBuilderParam: @memo() (() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within Builder property\", undefined, undefined); + })); + } + })); + this.myBuilderMethod(); + }), + }, undefined, undefined); + })); + } + public constructor() {} +} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_myBuilderParam = ((((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.myBuilderParam)})) ?? (content))) ?? ((() => { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, \"within BuilderParam property\", undefined, undefined); + })); + } + })); + }))) + } + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_myBuilderParam?: @memo() (()=> void); + public get myBuilderParam(): @memo() (()=> void) { + return this.__backing_myBuilderParam!; + } + public set myBuilderParam(value: @memo() (()=> void)) { + this.__backing_myBuilderParam = value; + } + @memo() public build() { + ConditionScope(@memo() (() => { + if (true) { + ConditionBranch(@memo() (() => { + Text(undefined, "within struct build", undefined, undefined); + })); + } + })); + } + public constructor() {} +} +@Component() export interface __Options_MyStruct { +} +@Component() export interface __Options_Child { + set myBuilderParam(myBuilderParam: (@memo() (()=> void) | undefined)) + get myBuilderParam(): (@memo() (()=> void) | undefined) +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { ConditionScope as ConditionScope } from \"arkui.component.builder\"; +import { ConditionBranch as ConditionBranch } from \"arkui.component.builder\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from \"@ohos.arkui.component\"; +const wBuilder = wrapBuilder(ParamBuilder); +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder function\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +} +@memo() function ParamBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @Builder() @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + let param: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder parameter\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + }) as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_param = __memo_scope.param(0, param); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_param.value(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } +} +wBuilder = wrapBuilder(ParamBuilder); +@Component() final struct MyStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStruct | undefined)): void {} + @memo() public myBuilderMethod(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder method\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + ()), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + wBuilder.builder(__memo_context, ((__memo_id) + ()), @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"with Builder lambda\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + Child._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Child(); + }), { + myBuilderParam: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within Builder property\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + this.myBuilderMethod(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_myBuilderParam = ((((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.myBuilderParam)})) ?? (content))) ?? (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, \"within BuilderParam property\", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + }))) + } + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_myBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + public get myBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_myBuilderParam!; + } + public set myBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_myBuilderParam = value; + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (true) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), undefined, "within struct build", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Component() export interface __Options_MyStruct { +} +@Component() export interface __Options_Child { + set myBuilderParam(myBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + get myBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test conditionScope within @Builder or @BuilderParam', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts index b9a0d5943b3976263962533eb7f9fc86253eb8a2..3215144c70cbcc6cc9e953ae3c8b4c42167c26c5 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -27,55 +28,67 @@ const CUSTOM_COMPONENT_DIR_PATH: string = 'custom-component'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, CUSTOM_COMPONENT_DIR_PATH, 'custom-component-call.ets'), + path.resolve( + getRootPath(), + MOCK_ENTRY_DIR_PATH, + BUILDER_LAMBDA_DIR_PATH, + CUSTOM_COMPONENT_DIR_PATH, + 'custom-component-call.ets' + ), ]; const pluginTester = new PluginTester('test custom component call transformation', buildConfig); const parsedTransform: Plugins = { name: 'custom-component-call', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedParsedScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; -@Component() final class CustomContainer extends CustomComponent { +@Component() final struct CustomContainer extends CustomComponent { @Builder() public closerBuilder() {} - @BuilderParam() public closer: (()=> void) = (this).closerBuilder; + + @BuilderParam() public closer: (()=> void) = this.closerBuilder; + public build() {} + public constructor() {} + } -@Component() final class CustomContainerUser extends CustomComponent { +@Component() final struct CustomContainerUser extends CustomComponent { public build() { - Column(undefined){ - CustomContainer(undefined){ - Column(undefined){ - Text("hello", undefined); + Column(){ + CustomContainer(){ + Column(){ + Text("hello"); }; }; - CustomContainer(({} as __Options_CustomContainer)){ - Column(undefined){}; + CustomContainer({}){ + Column(){}; }; CustomContainer(undefined){}; CustomContainer(); }; } + public constructor() {} + } -interface __Options_CustomContainer { - closer?: @memo() (()=> void); +@Component() export interface __Options_CustomContainer { + @BuilderParam() closer?: (()=> void); + } -interface __Options_CustomContainerUser { +@Component() export interface __Options_CustomContainerUser { + } `; @@ -84,68 +97,85 @@ function testParedTransformer(this: PluginTestContext): void { } const expectedBuilderLambdaScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Text as Text, Column as Column, Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class CustomContainer extends CustomComponent { - public __initializeStruct(initializers: __Options_CustomContainer | undefined, @memo() content: (()=> void) | undefined): void { - (this).__backing_closer = ((((({let gensym___38813563 = initializers; - (((gensym___38813563) == (null)) ? undefined : gensym___38813563.closer)})) ?? (content))) ?? ((this).closerBuilder)) + + +@Component() final struct CustomContainer extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomContainer | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_closer = ((((({let gensym___38813563 = initializers; + (((gensym___38813563) == (null)) ? undefined : gensym___38813563.closer)})) ?? (content))) ?? (this.closerBuilder)) } - public __updateStruct(initializers: __Options_CustomContainer | undefined): void {} + + public __updateStruct(initializers: (__Options_CustomContainer | undefined)): void {} + private __backing_closer?: @memo() (()=> void); + public get closer(): @memo() (()=> void) { - return (this).__backing_closer!; + return this.__backing_closer!; } - public set closer(@memo() value: (()=> void)) { - (this).__backing_closer = value; + + public set closer(value: @memo() (()=> void)) { + this.__backing_closer = value; } + @memo() public closerBuilder() {} - @memo() public _build(@memo() style: ((instance: CustomContainer)=> CustomContainer) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CustomContainer | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class CustomContainerUser extends CustomComponent { - public __initializeStruct(initializers: __Options_CustomContainerUser | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_CustomContainerUser | undefined): void {} - @memo() public _build(@memo() style: ((instance: CustomContainerUser)=> CustomContainerUser) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CustomContainerUser | undefined): void { - Column(undefined, undefined, (() => { +@Component() final struct CustomContainerUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomContainerUser | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_CustomContainerUser | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), undefined, (() => { - Column(undefined, undefined, (() => { + }), undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { Text(undefined, "hello", undefined, undefined); })); - }), undefined); + })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), ({} as __Options_CustomContainer), (() => { - Column(undefined, undefined, (() => {})); - }), undefined); + }), {}, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => {})); + })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), undefined, (() => {}), undefined); + }), undefined, undefined, @memo() (() => {})); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); }), undefined, undefined, undefined); })); } + public constructor() {} + } -interface __Options_CustomContainer { - set closer(closer: @memo() (()=> void) | undefined) - get closer(): @memo() (()=> void) | undefined +@Component() export interface __Options_CustomContainer { + set closer(closer: (@memo() (()=> void) | undefined)) + + get closer(): (@memo() (()=> void) | undefined) + } -interface __Options_CustomContainerUser { +@Component() export interface __Options_CustomContainerUser { + } `; @@ -155,10 +185,10 @@ function testCustomComponentTransformer(this: PluginTestContext): void { pluginTester.run( 'test custom component call transformation', - [parsedTransform, recheck, uiNoRecheck], + [parsedTransform, recheck, uiNoRecheck, recheck], { parsed: [testParedTransformer], - 'checked:builder-lambda-no-recheck': [testCustomComponentTransformer], + 'checked:ui-no-recheck': [testCustomComponentTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts index 37b4605ad3ed31255840b0017cae248b3f3258fb..e8e1ace16621ce77238c023b35f9aec5457e3673 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda'; @@ -31,12 +32,13 @@ const pluginTester = new PluginTester('test builder-lambda simple component', bu function testBuilderLambdaTransformer(this: PluginTestContext): void { const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; -import { Column as Column, UIColumnAttribute as UIColumnAttribute } from \"arkui.component.column\"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { memo as memo } from \"@ohos.arkui.stateManagement\"; +import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { @memo() public build() { - Column(undefined, undefined, (() => {})); + Column(undefined, undefined, @memo() (() => {})); } public constructor() {} } @@ -46,17 +48,19 @@ class MyStateSample { function testMemoTransformer(this: PluginTestContext): void { const expectedScript: string = ` -import { memo as memo, __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"@ohos.arkui.stateManagement\"; -import { Column as Column, UIColumnAttribute as UIColumnAttribute } from \"arkui.component.column\"; +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { memo as memo } from \"@ohos.arkui.stateManagement\"; +import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + (263357132)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (147296800)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,7 +84,7 @@ class MyStateSample { pluginTester.run( 'transform simple component', - [builderLambdaNoRecheck, recheck, memoNoRecheck], + [builderLambdaNoRecheck, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:builder-lambda-no-recheck': [testBuilderLambdaTransformer], 'checked:memo-no-recheck': [testMemoTransformer], diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts index 1c64f1cdb615ab39691453a1af0eea3b08742de5..e55e43771de8765654481ac179701454e6240003 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { builderLambdaNoRecheck, structNoRecheck, uiNoRecheck } from '../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { uiTransform } from '../../../../ui-plugins'; import { Plugins } from '../../../../common/plugin-context'; @@ -33,44 +34,51 @@ const pluginTester = new PluginTester('test function with receiver style transfo const parsedTransform: Plugins = { name: 'style-with-receiver', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { memo as memo } from "@ohos.arkui.stateManagement"; -import { Text as Text, UITextAttribute as UITextAttribute, Column as Column, Component as Component } from "@ohos.arkui.component"; + +import { Text as Text, TextAttribute as TextAttribute, Column as Column, Component as Component } from "@ohos.arkui.component"; + import hilog from "@ohos.hilog"; function main() {} -@memo() function cardStyle(this: UITextAttribute, num: number, str: string): UITextAttribute { + +@memo() function cardStyle(this: TextAttribute, num: number, str: string): TextAttribute { this.fontSize(num); this.backgroundColor(num); return this; } -@memo() function style22(this: UITextAttribute): UITextAttribute { +@memo() function style22(this: TextAttribute): TextAttribute { this.fontWeight(700); return this; } -@Component({freezeWhenInactive:false}) final class MM extends CustomComponent { - public __initializeStruct(initializers: __Options_MM | undefined, @memo() content: (()=> void) | undefined): void {} + +@Component() final struct MM extends CustomComponent { + public __initializeStruct(initializers: (__Options_MM | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MM | undefined): void {} + public __updateStruct(initializers: (__Options_MM | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MM)=> MM) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MM | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { style22(cardStyle(instance.height(200).fontColor("#000000"), 600, "#eeeeee").fontSize(60).fontWeight(400)).width(900); return; }), "hello world", undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { cardStyle(instance, 600, "#eeeeee"); return; }), "hello world", undefined, undefined); @@ -81,7 +89,7 @@ function main() {} } -interface __Options_MM { +@Component() export interface __Options_MM { } `; @@ -92,9 +100,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test function with receiver style transformstion', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..6261ad5d528fcbf90be7e222a349ac309d78c360 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const COMPONENT_DIR_PATH: string = 'component'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMPONENT_DIR_PATH, 'declare-component.ets'), +]; + +const pluginTester = new PluginTester('test declare component transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'declare-component', + parsed: uiTransform().parsed +}; + +const expectedParsedcript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; + +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; + +@Component() export declare final struct SwipeRefresher extends CustomComponent { + @Prop() public content?: (ResourceStr | undefined); + + @Prop() public isLoading: boolean; + + @State() public code: number; + + @Builder() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_SwipeRefresher): void + +} + +@Component() export declare interface __Options_SwipeRefresher { + content?: (ResourceStr | undefined); + @Prop() __backing_content?: (ResourceStr | undefined); + isLoading?: boolean; + @Prop() __backing_isLoading?: boolean; + code?: number; + @State() __backing_code?: number; + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedcript)); +} + +const expectedCheckedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; + +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() export declare final struct SwipeRefresher extends CustomComponent { + @Prop() public content?: (ResourceStr | undefined); + + @Prop() public isLoading: boolean; + + @State() public code: number; + + @Builder() @memo() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_SwipeRefresher): void + +} + +@Component() export declare interface __Options_SwipeRefresher { + set content(content: ((ResourceStr | undefined) | undefined)) + + get content(): ((ResourceStr | undefined) | undefined) + set __backing_content(__backing_content: (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined)) + + get __backing_content(): (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined) + set isLoading(isLoading: (boolean | undefined)) + + get isLoading(): (boolean | undefined) + set __backing_isLoading(__backing_isLoading: (IPropDecoratedVariable | undefined)) + + get __backing_isLoading(): (IPropDecoratedVariable | undefined) + set code(code: (number | undefined)) + + get code(): (number | undefined) + set __backing_code(__backing_code: (IStateDecoratedVariable | undefined)) + + get __backing_code(): (IStateDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test declare component transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts b/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..52e9fd7b7c49db71da2ee588630d54098c043c69 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const COMPONENT_DIR_PATH: string = 'component'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, COMPONENT_DIR_PATH, 'for-each.ets'), +]; + +const pluginTester = new PluginTester('test ForEach component transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'for-each', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +function main() {} + +interface Person { + set name(name: string) + + get name(): string + set age(age: number) + + get age(): number + +} + +class AB { + public per: string = "hello"; + + public bar: Array = new Array("xx", "yy", "zz"); + + public constructor() {} + +} + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arr = ((({let gensym___244068973 = initializers; + (((gensym___244068973) == (null)) ? undefined : gensym___244068973.arr)})) ?? (["a", "b", "c"])); + } + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + private __backing_arr?: Array; + + public get arr(): Array { + return (this.__backing_arr as Array); + } + + public set arr(value: Array) { + this.__backing_arr = value; + } + + public getArray() { + return new Array(({ + name: "LiHua", + age: 25, + } as Person), ({ + name: "Amy", + age: 18, + } as Person)); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.arr; + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + ForEach(((): Array => { + return this.getArray(); + }), ((item: Person) => { + Text(undefined, item.name, undefined, undefined); + })); + ForEach(((): Array => { + return new AB().bar; + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + ForEach(((): Array => { + return new AB().bar; + }), (() => {})); + ForEach(((): Array => { + return this.getArray(); + }), (() => {})); + ForEach(((): Array => { + return new Array("1", "2"); + }), (() => { + ForEach(((): Array => { + return new Array("1", "2"); + }), ((item: string) => { + Text(undefined, item, undefined, undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + set arr(arr: (Array | undefined)) + + get arr(): (Array | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test ForEach component transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts index ba63b9e2d3f79a811fb07922b3c14d811a15a9d8..26590b874bc38d4685df10a36b88ed812dba11b9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -33,99 +34,254 @@ const pluginTester = new PluginTester('test builder param variable passing', bui const parsedTransform: Plugins = { name: 'builder-param-passing', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; -const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "@ohos.arkui.stateManagement"; -import { __memo_context_type as __memo_context_type } from "@ohos.arkui.stateManagement"; -import { memo as memo } from "@ohos.arkui.stateManagement"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { CustomComponent as CustomComponent } from "@ohos.arkui.component"; +const expectedAfterUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - (this).__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; - (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? ((this).customBuilder)) +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.customBuilder)) } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void {} private __backing_customBuilderParam?: @memo() (()=> void); public get customBuilderParam(): @memo() (()=> void) { - return (this).__backing_customBuilderParam!; + return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { - (this).__backing_customBuilderParam = value; + public set customBuilderParam(value: @memo() (()=> void)) { + this.__backing_customBuilderParam = value; } @memo() public customBuilder() {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { - (this).customBuilderParam(); + @memo() public build() { + this.customBuilderParam(); } public constructor() {} } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_Parent | undefined): void {} + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} @memo() public componentBuilder() { Text(undefined, "Parent builder", undefined, undefined); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void { - Column(undefined, undefined, (() => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ - customBuilderParam: (this).componentBuilder, - } as __Options_Child), undefined, undefined); + }), { + customBuilderParam: this.componentBuilder, + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { customBuilderParam: @memo() (() => { - (this).componentBuilder(); + this.componentBuilder(); }), - } as __Options_Child), undefined, undefined); + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), undefined, (() => { + }), undefined, undefined, @memo() (() => { Text(undefined, "Parent builder", undefined, undefined); - }), undefined); + })); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() (()=> void) | undefined)) + + get customBuilderParam(): (@memo() (()=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +const expectedAfterMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.customBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam!; + } + + public set customBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam = value; + } + + @memo() public customBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (252759234)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (209256344)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam(__memo_context, ((__memo_id) + (175145513))); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (219399173)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (137225318)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (135515930)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (136716185)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (54078781)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Child._instantiateImpl(__memo_context, ((__memo_id) + (213104625)), undefined, (() => { + return new Child(); + }), { + customBuilderParam: this.componentBuilder, + }, undefined, undefined); + Child._instantiateImpl(__memo_context, ((__memo_id) + (218979098)), undefined, (() => { + return new Child(); + }), { + customBuilderParam: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.componentBuilder(__memo_context, ((__memo_id) + (46726221))); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + Child._instantiateImpl(__memo_context, ((__memo_id) + (213687742)), undefined, (() => { + return new Child(); + }), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (192802443)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (223657391)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } })); + { + __memo_scope.recache(); + return; + } } + public constructor() {} + } -interface __Options_Child { - set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) - get customBuilderParam(): @memo() (()=> void) | undefined +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + } -interface __Options_Parent { +@Component() export interface __Options_Parent { + } `; function testCheckedTransformer(this: PluginTestContext): void { - expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterUIScript)); +} + +function testMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterMemoScript)); } pluginTester.run( 'test builder param variable passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], + 'checked:memo-no-recheck': [testMemoCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts index 21e7f2bb4fa8bba3df65ba934b73b5ace27df3c1..b480c115489bc4484e8a36e36d7fe1b9d98ef800 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -36,63 +37,172 @@ const parsedTransform: Plugins = { parsed: uiTransform().parsed }; -const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +const expectedAfterUIScript: string = ` import { memo as memo } from "arkui.stateManagement.runtime"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; - function main() {} -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.doNothingBuilder)) this.__backing_customBuilderParam2 = ((((({let gensym___14041256 = initializers; (((gensym___14041256) == (null)) ? undefined : gensym___14041256.customBuilderParam2)})) ?? (content))) ?? (this.doNothingBuilder2)) } - public __updateStruct(initializers: __Options_Child | undefined): void {} + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + private __backing_customBuilderParam?: @memo() (()=> void); + public get customBuilderParam(): @memo() (()=> void) { return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { + + public set customBuilderParam(value: @memo() (()=> void)) { this.__backing_customBuilderParam = value; } + private __backing_customBuilderParam2?: @memo() ((str: string)=> void); + public get customBuilderParam2(): @memo() ((str: string)=> void) { return this.__backing_customBuilderParam2!; } - public set customBuilderParam2(@memo() value: ((str: string)=> void)) { + + public set customBuilderParam2(value: @memo() ((str: string)=> void)) { this.__backing_customBuilderParam2 = value; } + @memo() public doNothingBuilder() {} + @memo() public doNothingBuilder2(str: string) {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { this.customBuilderParam(); this.customBuilderParam2("hello"); } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() (()=> void) | undefined)) + + get customBuilderParam(): (@memo() (()=> void) | undefined) + set customBuilderParam2(customBuilderParam2: (@memo() ((str: string)=> void) | undefined)) + + get customBuilderParam2(): (@memo() ((str: string)=> void) | undefined) + +} +`; + +const expectedAfterMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Builder as Builder, BuilderParam as BuilderParam } from "@ohos.arkui.component"; + +function main() {} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam = ((((({let gensym___169376706 = initializers; + (((gensym___169376706) == (null)) ? undefined : gensym___169376706.customBuilderParam)})) ?? (content))) ?? (this.doNothingBuilder)) + this.__backing_customBuilderParam2 = ((((({let gensym___14041256 = initializers; + (((gensym___14041256) == (null)) ? undefined : gensym___14041256.customBuilderParam2)})) ?? (content))) ?? (this.doNothingBuilder2)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam!; + } + + public set customBuilderParam(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam = value; + } + + private __backing_customBuilderParam2?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void); + + public get customBuilderParam2(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) { + return this.__backing_customBuilderParam2!; + } + + public set customBuilderParam2(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void)) { + this.__backing_customBuilderParam2 = value; + } + + @memo() public doNothingBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (174403279)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public doNothingBuilder2(__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string) { + const __memo_scope = __memo_context.scope(((__memo_id) + (76253767)), 1); + const __memo_parameter_str = __memo_scope.param(0, str); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (179390036)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam(__memo_context, ((__memo_id) + (241913892))); + this.customBuilderParam2(__memo_context, ((__memo_id) + (137225318)), "hello"); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} + } -interface __Options_Child { - set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) - get customBuilderParam(): @memo() (()=> void) | undefined - set customBuilderParam2(customBuilderParam2: @memo() ((str: string)=> void) | undefined) - get customBuilderParam2(): @memo() ((str: string)=> void) | undefined +@Component() export interface __Options_Child { + set customBuilderParam(customBuilderParam: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + set customBuilderParam2(customBuilderParam2: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) | undefined)) + + get customBuilderParam2(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string)=> void) | undefined) + } `; function testCheckedTransformer(this: PluginTestContext): void { - expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterUIScript)); +} + +function testAfterMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterMemoScript)); } pluginTester.run( 'test builder param init with local builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], + 'checked:memo-no-recheck': [testAfterMemoCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..68b761ca7ba361ab6ed07f910ee8aed00ad5b032 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts @@ -0,0 +1,372 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/builder-param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'optional-builder-param.ets'), +]; + +const pluginTester = new PluginTester('test optional builder param', buildConfig); + +const parsedTransform: Plugins = { + name: 'optional-builder-param', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text, Row as Row } from "@kit.ArkUI"; + +function main() {} + +@memo() function showTextBuilder() { + Text(undefined, "Hello World", undefined, undefined); +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_customBuilderParam2 = ((((({let gensym___103851375 = initializers; + (((gensym___103851375) == (null)) ? undefined : gensym___103851375.customBuilderParam2)})) ?? (content))) ?? (undefined)) + this.__backing_customBuilderParam1 = ((((({let gensym___20169645 = initializers; + (((gensym___20169645) == (null)) ? undefined : gensym___20169645.customBuilderParam1)})) ?? (content))) ?? (showTextBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam2?: ((()=> void) | undefined); + + public get customBuilderParam2(): (@memo() (()=> void) | undefined) { + return this.__backing_customBuilderParam2; + } + + public set customBuilderParam2(value: (@memo() (()=> void) | undefined)) { + this.__backing_customBuilderParam2 = value; + } + + private __backing_customBuilderParam1?: @memo() (()=> void); + + public get customBuilderParam1(): @memo() (()=> void) { + return this.__backing_customBuilderParam1!; + } + + public set customBuilderParam1(value: @memo() (()=> void)) { + this.__backing_customBuilderParam1 = value; + } + + @memo() public build() { + Row(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (this.customBuilderParam2) { + ConditionBranch(@memo() (() => { + (this.customBuilderParam2 as (()=> void))(); + })); + } + })); + ConditionScope(@memo() (() => { + if (this.customBuilderParam2) { + ConditionBranch(@memo() (() => { + this.customBuilderParam2!(); + })); + } + })); + this.customBuilderParam1(); + })); + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder() { + Text(undefined, "Parent builder", undefined, undefined); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + customBuilderParam2: @memo() (() => { + this.componentBuilder(); + }), + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam2(customBuilderParam2: (((()=> void) | undefined) | undefined)) + + get customBuilderParam2(): (((()=> void) | undefined) | undefined) + set customBuilderParam1(customBuilderParam1: (@memo() (()=> void) | undefined)) + + get customBuilderParam1(): (@memo() (()=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { ConditionScope as ConditionScope } from "arkui.component.builder"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry, Builder as Builder, BuilderParam as BuilderParam, Column as Column, Text as Text, Row as Row } from "@kit.ArkUI"; + +function main() {} + +@memo() function showTextBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (183537441)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (47330804)), undefined, "Hello World", undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_customBuilderParam2 = ((((({let gensym___103851375 = initializers; + (((gensym___103851375) == (null)) ? undefined : gensym___103851375.customBuilderParam2)})) ?? (content))) ?? (undefined)) + this.__backing_customBuilderParam1 = ((((({let gensym___20169645 = initializers; + (((gensym___20169645) == (null)) ? undefined : gensym___20169645.customBuilderParam1)})) ?? (content))) ?? (showTextBuilder)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_customBuilderParam2?: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined); + + public get customBuilderParam2(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) { + return this.__backing_customBuilderParam2; + } + + public set customBuilderParam2(value: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) { + this.__backing_customBuilderParam2 = value; + } + + private __backing_customBuilderParam1?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + + public get customBuilderParam1(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) { + return this.__backing_customBuilderParam1!; + } + + public set customBuilderParam1(value: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { + this.__backing_customBuilderParam1 = value; + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (234402485)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + (46726221)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213104625)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (this.customBuilderParam2) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + (this.customBuilderParam2 as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + ConditionScope(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (this.customBuilderParam2) { + ConditionBranch(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.customBuilderParam2!(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + })); + } + { + __memo_scope.recache(); + return; + } + })); + this.customBuilderParam1(__memo_context, ((__memo_id) + (211301233))); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + @memo() public componentBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (179117969)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (218979098)), undefined, "Parent builder", undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (245938697)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (78055758)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (136716185)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Child._instantiateImpl(__memo_context, ((__memo_id) + (54078781)), undefined, (() => { + return new Child(); + }), { + customBuilderParam2: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213687742)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.componentBuilder(__memo_context, ((__memo_id) + (192802443))); + { + __memo_scope.recache(); + return; + } + }), + }, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set customBuilderParam2(customBuilderParam2: ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | undefined)) + + get customBuilderParam2(): ((((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) | undefined) + set customBuilderParam1(customBuilderParam1: (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)) + + get customBuilderParam1(): (@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) + +} + +@Component() export interface __Options_Parent { + +} +`; + +function testUICheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +function testMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test optional builder param', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUICheckedTransformer], + 'checked:memo-no-recheck': [testMemoCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts index 95740a1d5123337008002dcf8760639e48454ce0..478bc834a5034aab34a6747da1b806626eae9e88 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts @@ -14,58 +14,63 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'decorators/builder'; const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'global-builder.ets'), -]; +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'global-builder.ets')]; const pluginTester = new PluginTester('test global builder', buildConfig); const parsedTransform: Plugins = { name: 'global-builder', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIRowAttribute as UIRowAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Row as Row, Builder as Builder, Text as Text } from "@ohos.arkui.component"; function main() {} + @memo() function showTextBuilder() { Text(undefined, "Hello World", undefined, undefined); } @memo() function overBuilder(params: Tmp) { - Row(undefined, undefined, (() => { + Row(undefined, undefined, @memo() (() => { Text(undefined, (("UseStateVarByReference: ") + (params.paramA1)), undefined, undefined); })); } + class Tmp { public paramA1: string = ""; + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class BuilderDemo extends CustomComponent { - public __initializeStruct(initializers: __Options_BuilderDemo | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} - @memo() public _build(@memo() style: ((instance: BuilderDemo)=> BuilderDemo) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_BuilderDemo | undefined): void { +@Component() final struct BuilderDemo extends CustomComponent { + public __initializeStruct(initializers: (__Options_BuilderDemo | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_BuilderDemo | undefined)): void {} + + @memo() public build() { Row(undefined, undefined, @memo() (() => { showTextBuilder(); overBuilder({ @@ -73,10 +78,12 @@ class Tmp { }); })); } + public constructor() {} + } -interface __Options_BuilderDemo { +@Component() export interface __Options_BuilderDemo { } `; @@ -87,7 +94,7 @@ function testCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'global builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts index 9769285c5dff301ccb5500da9ed6655cd95f1c4d..e4ece6f3a997bbddd84343021512d5d42b084f27 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts @@ -14,64 +14,73 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'decorators/builder'; const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'local-builder.ets'), -]; +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'local-builder.ets')]; const pluginTester = new PluginTester('test local builder', buildConfig); const parsedTransform: Plugins = { name: 'local-builder', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, Builder as Builder, Text as Text } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class BuilderDemo extends CustomComponent { - public __initializeStruct(initializers: __Options_BuilderDemo | undefined, @memo() content: (()=> void) | undefined): void {} - public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} + + +@Component() final struct BuilderDemo extends CustomComponent { + public __initializeStruct(initializers: (__Options_BuilderDemo | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_BuilderDemo | undefined)): void {} + @memo() public showTextBuilder() { - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), "Hello World", undefined, undefined); } + @memo() public showTextValueBuilder(param: string) { - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), param, undefined, undefined); } - @memo() public _build(@memo() style: ((instance: BuilderDemo)=> BuilderDemo) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_BuilderDemo | undefined): void { + + @memo() public build() { Column(undefined, undefined, @memo() (() => { this.showTextBuilder(); this.showTextValueBuilder("Hello @Builder"); })); } + public constructor() {} + } -interface __Options_BuilderDemo { +@Component() export interface __Options_BuilderDemo { } `; @@ -82,7 +91,7 @@ function testCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'local builder', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { 'checked:ui-no-recheck': [testCheckedTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd46414df8dd29538e2eea80e8dce6bbc26cb35a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-observedv2-class.test.ts @@ -0,0 +1,141 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-in-observedv2-class.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator in @ObservedV2 class', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) private __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() private __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) private __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() private __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + console.info("---------Computed----------"); + return ((((this.firstName) + (" "))) + (this.lastName)); + }), "fullName"); + + @Computed() public get fullName(): string { + return this.__computed_fullName!.get(); + } + + public get firstName(): string { + this.conditionalAddRef(this.__meta_firstName); + return UIUtils.makeObserved(this.__backing_firstName); + } + + public set firstName(newValue: string) { + if (((this.__backing_firstName) !== (newValue))) { + this.__backing_firstName = newValue; + this.__meta_firstName.fireChange(); + this.executeOnSubscribingWatches("firstName"); + } + } + + public get lastName(): string { + this.conditionalAddRef(this.__meta_lastName); + return UIUtils.makeObserved(this.__backing_lastName); + } + + public set lastName(newValue: string) { + if (((this.__backing_lastName) !== (newValue))) { + this.__backing_lastName = newValue; + this.__meta_lastName.fireChange(); + this.executeOnSubscribingWatches("lastName"); + } + } + + public constructor() {} + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator in @ObservedV2 class', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e904a0ed431c395c412c7807f1184501ee2d0ff6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-in-struct.test.ts @@ -0,0 +1,165 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-in-struct.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator in @ComponentV2 struct', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button, Divider as Divider, Text as Text } from "@ohos.arkui.component"; + +import { Computed as Computed, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_firstName = STATE_MGMT_FACTORY.makeLocal(this, "firstName", "Li"); + this.__backing_lastName = STATE_MGMT_FACTORY.makeLocal(this, "lastName", "Hua"); + this.__backing_age = ((({let gensym___216981064 = initializers; + (((gensym___216981064) == (null)) ? undefined : gensym___216981064.age)})) ?? (20)); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_firstName?: ILocalDecoratedVariable; + + public get firstName(): string { + return this.__backing_firstName!.get(); + } + + public set firstName(value: string) { + this.__backing_firstName!.set(value); + } + + private __backing_lastName?: ILocalDecoratedVariable; + + public get lastName(): string { + return this.__backing_lastName!.get(); + } + + public set lastName(value: string) { + this.__backing_lastName!.set(value); + } + + private __backing_age?: number; + + public get age(): number { + return (this.__backing_age as number); + } + + public set age(value: number) { + this.__backing_age = value; + } + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + console.info("---------Computed----------"); + return ((((((this.firstName) + (" "))) + (this.lastName))) + (this.age)); + }), "fullName"); + + @Computed() public get fullName(): string { + return this.__computed_fullName!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, ((((this.lastName) + (" "))) + (this.firstName)), undefined, undefined); + Text(undefined, ((((this.lastName) + (" "))) + (this.firstName)), undefined, undefined); + Divider(undefined); + Text(undefined, this.fullName, undefined, undefined); + Text(undefined, this.fullName, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.lastName += "a"; + })); + return; + }), "changed lastName", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.age++); + })); + return; + }), "changed age", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set firstName(firstName: (string | undefined)) + + get firstName(): (string | undefined) + set __backing_firstName(__backing_firstName: (ILocalDecoratedVariable | undefined)) + + get __backing_firstName(): (ILocalDecoratedVariable | undefined) + set lastName(lastName: (string | undefined)) + + get lastName(): (string | undefined) + set __backing_lastName(__backing_lastName: (ILocalDecoratedVariable | undefined)) + + get __backing_lastName(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator in @ComponentV2 struct', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c592e5333227dab8155878136c325b9113c8a18e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/computed-no-return-type.test.ts @@ -0,0 +1,241 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'computed-no-return-type.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorator with no return type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button, Divider as Divider, Text as Text } from "@ohos.arkui.component"; + +import { Computed as Computed, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) private __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() private __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) private __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() private __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 20; + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed<(Array | string)>((() => { + if (((this.age) >= (20))) { + return new Array(0, 1, 2); + } + return ((((this.firstName) + (" "))) + (this.lastName)); + }), "fullName"); + + @Computed() public get fullName(): (Array | string) { + return this.__computed_fullName!.get(); + } + + public get firstName(): string { + this.conditionalAddRef(this.__meta_firstName); + return UIUtils.makeObserved(this.__backing_firstName); + } + + public set firstName(newValue: string) { + if (((this.__backing_firstName) !== (newValue))) { + this.__backing_firstName = newValue; + this.__meta_firstName.fireChange(); + this.executeOnSubscribingWatches("firstName"); + } + } + + public get lastName(): string { + this.conditionalAddRef(this.__meta_lastName); + return UIUtils.makeObserved(this.__backing_lastName); + } + + public set lastName(newValue: string) { + if (((this.__backing_lastName) !== (newValue))) { + this.__backing_lastName = newValue; + this.__meta_lastName.fireChange(); + this.executeOnSubscribingWatches("lastName"); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_firstName = STATE_MGMT_FACTORY.makeLocal(this, "firstName", "Li"); + this.__backing_lastName = STATE_MGMT_FACTORY.makeLocal(this, "lastName", "Hua"); + this.__backing_age = ((({let gensym___216981064 = initializers; + (((gensym___216981064) == (null)) ? undefined : gensym___216981064.age)})) ?? (20)); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_firstName?: ILocalDecoratedVariable; + + public get firstName(): string { + return this.__backing_firstName!.get(); + } + + public set firstName(value: string) { + this.__backing_firstName!.set(value); + } + + private __backing_lastName?: ILocalDecoratedVariable; + + public get lastName(): string { + return this.__backing_lastName!.get(); + } + + public set lastName(value: string) { + this.__backing_lastName!.set(value); + } + + private __backing_age?: number; + + public get age(): number { + return (this.__backing_age as number); + } + + public set age(value: number) { + this.__backing_age = value; + } + + private __computed_fullName = STATE_MGMT_FACTORY.makeComputed<(Int | string)>((() => { + if (((this.age) >= (20))) { + return 500; + } + return ((((((this.firstName) + (" "))) + (this.lastName))) + (this.age)); + }), "fullName"); + + @Computed() public get fullName(): (Int | string) { + return this.__computed_fullName!.get(); + } + + private __computed_num5 = STATE_MGMT_FACTORY.makeComputed((() => { + return 5; + }), "num5"); + + @Computed() public get num5(): Int { + return this.__computed_num5!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set firstName(firstName: (string | undefined)) + + get firstName(): (string | undefined) + set __backing_firstName(__backing_firstName: (ILocalDecoratedVariable | undefined)) + + get __backing_firstName(): (ILocalDecoratedVariable | undefined) + set lastName(lastName: (string | undefined)) + + get lastName(): (string | undefined) + set __backing_lastName(__backing_lastName: (ILocalDecoratedVariable | undefined)) + + get __backing_lastName(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorator with no return type', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d7331ed63ae4cd279816b51c602f0c4a61d31eb --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/computed/static-computed.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/computed'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'static-computed.ets'), +]; + +const pluginTester = new PluginTester('test @Computed decorated static getter method', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Computed as Computed, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class Name implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"firstName"}) public static __backing_firstName: string = "Hua"; + + @JSONStringifyIgnore() public static __meta_firstName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"lastName"}) public static __backing_lastName: string = "Li"; + + @JSONStringifyIgnore() public static __meta_lastName: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Name.__computed_fullName.get(); + } + + static { + + } + public static get firstName(): string { + Name.__meta_firstName.addRef(); + return UIUtils.makeObserved(Name.__backing_firstName); + } + + public static set firstName(newValue: string) { + if (((Name.__backing_firstName) !== (newValue))) { + Name.__backing_firstName = newValue; + Name.__meta_firstName.fireChange(); + } + } + + public static get lastName(): string { + Name.__meta_lastName.addRef(); + return UIUtils.makeObserved(Name.__backing_lastName); + } + + public static set lastName(newValue: string) { + if (((Name.__backing_lastName) !== (newValue))) { + Name.__backing_lastName = newValue; + Name.__meta_lastName.fireChange(); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + public static __backing_localVar1: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar1", "stateVar1"); + + public static get localVar1(): string { + return Parent.__backing_localVar1.get(); + } + + public static set localVar1(value: string) { + Parent.__backing_localVar1.set(value); + } + + public static __backing_localVar2: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar2", 50); + + public static get localVar2(): number { + return Parent.__backing_localVar2.get(); + } + + public static set localVar2(value: number) { + Parent.__backing_localVar2.set(value); + } + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return Parent.localVar1; + }), "fullName"); + + @Computed() public static get fullName(): string { + return Parent.__computed_fullName.get(); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ComponentV2() final struct Parent2 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent2 | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent2 | undefined)): void {} + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Parent2.__computed_fullName.get(); + } + + public static __computed_fullName2 = STATE_MGMT_FACTORY.makeComputed((() => { + return Parent.localVar1; + }), "fullName2"); + + @Computed() public static get fullName2(): string { + return Parent2.__computed_fullName2.get(); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ObservedV2() class Name2 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public static __computed_fullName = STATE_MGMT_FACTORY.makeComputed((() => { + return ((((Name.firstName) + (" "))) + (Name.lastName)); + }), "fullName"); + + @Computed() public static get fullName(): string { + return Name2.__computed_fullName.get(); + } + + static { + + } + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_Parent2 { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Computed decorated static getter method', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e22b1f8c853bbd17a5979b0ad19306cb3c52ba2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/base-custom-dialog.test.ts @@ -0,0 +1,286 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'base-custom-dialog.ets'), +]; + +const pluginTester = new PluginTester('test basic capability of @CustomDialog', buildConfig); + +const parsedTransform: Plugins = { + name: 'base-custom-dialog', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Entry as Entry, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, Link as Link, Prop as Prop } from "@ohos.arkui.stateManagement"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, DismissDialogAction as DismissDialogAction, DismissReason as DismissReason, DialogAlignment as DialogAlignment, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@ohos.arkui.component"; + +import hilog from "@ohos.hilog"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + this.__backing_text = STATE_MGMT_FACTORY.makeState(this, "text", ((({let gensym___217676902 = initializers; + (((gensym___217676902) == (null)) ? undefined : gensym___217676902.text)})) ?? ("text"))); + this.__backing_cancel = ((({let gensym___25471281 = initializers; + (((gensym___25471281) == (null)) ? undefined : gensym___25471281.cancel)})) ?? ((() => {}))); + this.__backing_confirm = ((({let gensym___213253394 = initializers; + (((gensym___213253394) == (null)) ? undefined : gensym___213253394.confirm)})) ?? ((() => {}))); + this.__backing_hh = STATE_MGMT_FACTORY.makeState(this, "hh", ((({let gensym___210200872 = initializers; + (((gensym___210200872) == (null)) ? undefined : gensym___210200872.hh)})) ?? ("nihao"))); + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + private __backing_text?: IStateDecoratedVariable; + + public get text(): string { + return this.__backing_text!.get(); + } + + public set text(value: string) { + this.__backing_text!.set(value); + } + + private __backing_cancel?: (()=> void); + + public get cancel(): (()=> void) { + return (this.__backing_cancel as (()=> void)); + } + + public set cancel(value: (()=> void)) { + this.__backing_cancel = value; + } + + private __backing_confirm?: (()=> void); + + public get confirm(): (()=> void) { + return (this.__backing_confirm as (()=> void)); + } + + public set confirm(value: (()=> void)) { + this.__backing_confirm = value; + } + + private __backing_hh?: IStateDecoratedVariable; + + public get hh(): string { + return this.__backing_hh!.get(); + } + + public set hh(value: string) { + this.__backing_hh!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30).height(100); + return; + }), "CustomDialog One", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.aaController) != (undefined))) { + this.aaController!.close(); + } + })).margin(20); + return; + }), "Close", undefined, undefined); + })); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___56650533 = initializers; + (((gensym___56650533) == (null)) ? undefined : gensym___56650533.dialogController)})) ?? (({let gensym___249621102: Any; + gensym___249621102 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___249621102 as CustomDialogController)); + return instance; + }), { + cancel: (() => { + this.onCancel(); + }), + confirm: (() => { + this.onAccept(); + }), + }, undefined); + }), + cancel: this.existApp, + autoCancel: true, + alignment: DialogAlignment.Center, + offset: { + dx: 0, + dy: -20, + }, + gridCount: 4, + showInSubWindow: true, + isModal: true, + customStyle: false, + cornerRadius: 10, + focusable: true, + baseComponent: this, + }) + (gensym___249621102 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + public aboutToDisappear() { + this.dialogController = null; + } + + public onCancel() { + console.info("Callback when the first button is clicked"); + } + + public onAccept() { + console.info("Callback when the second button is clicked"); + } + + public existApp() { + console.info("Click the callback in the blank area"); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.dialogController) != (null))) { + this.dialogController!.open(); + } + })).backgroundColor(0x317aff); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set cancel(cancel: ((()=> void) | undefined)) + + get cancel(): ((()=> void) | undefined) + set confirm(confirm: ((()=> void) | undefined)) + + get confirm(): ((()=> void) | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + set __backing_hh(__backing_hh: (IStateDecoratedVariable | undefined)) + + get __backing_hh(): (IStateDecoratedVariable | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic capability of @CustomDialog', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d02a2478390192674a6beecf21db972795a4f56 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/builder-dialog-options.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'builder-dialog-options.ets'), +]; + +const pluginTester = new PluginTester('test CustomDialogControllerOptions with @Builder parameter', buildConfig); + +const parsedTransform: Plugins = { + name: 'builder-dialog-options', + parsed: uiTransform().parsed, +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +@Builder() function builder1(str: string) {} + +@Builder() function builder2() {} + +@Component() final struct CustomDialogUser extends CustomComponent { + public dialogController: (CustomDialogController | null) = new CustomDialogController({ + builder: (() => { + builder1("nihao"); + }), + }); + + public build() { + Column(){}; + } + + public constructor() {} + +} + +@Component() final struct CustomDialogUser2 extends CustomComponent { + public dialogController: (CustomDialogController | null) = new CustomDialogController({ + builder: builder2, + }); + + public build() { + Column(){}; + } + + public constructor() {} + +} + +@Component() export interface __Options_CustomDialogUser { + dialogController?: (CustomDialogController | null); + +} + +@Component() export interface __Options_CustomDialogUser2 { + dialogController?: (CustomDialogController | null); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +function main() {} + +@memo() function builder1(str: string) {} + +@memo() function builder2() {} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___51459619 = initializers; + (((gensym___51459619) == (null)) ? undefined : gensym___51459619.dialogController)})) ?? (({let gensym___203542966: Any; + gensym___203542966 = new CustomDialogController({ + builder: @memo() (() => { + builder1("nihao"); + }), + baseComponent: this, + }) + (gensym___203542966 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + +} + +@Component() final struct CustomDialogUser2 extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: builder2, + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser2 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser2 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test CustomDialogControllerOptions with @Builder parameter', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ecad395ed4a54f40e9f52a70b7424daf6011c66d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-build.test.ts @@ -0,0 +1,191 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'controller-in-build.ets'), +]; + +const pluginTester = new PluginTester('test CustomDialogController in build', buildConfig); + +const parsedTransform: Plugins = { + name: 'controller-in-build', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, Link as Link, Prop as Prop } from "@ohos.arkui.stateManagement"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +import hilog from "@ohos.hilog"; + +function main() {} + + + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + this.__backing_text = STATE_MGMT_FACTORY.makeState(this, "text", ((({let gensym___217676902 = initializers; + (((gensym___217676902) == (null)) ? undefined : gensym___217676902.text)})) ?? ("text"))); + this.__backing_hh = STATE_MGMT_FACTORY.makeState(this, "hh", ((({let gensym___112288773 = initializers; + (((gensym___112288773) == (null)) ? undefined : gensym___112288773.hh)})) ?? ("nihao"))); + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + private __backing_text?: IStateDecoratedVariable; + + public get text(): string { + return this.__backing_text!.get(); + } + + public set text(value: string) { + this.__backing_text!.set(value); + } + + private __backing_hh?: IStateDecoratedVariable; + + public get hh(): string { + return this.__backing_hh!.get(); + } + + public set hh(value: string) { + this.__backing_hh!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, "CustomDialog One", undefined, undefined); + })); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + let dialogController: (CustomDialogController | undefined) = ({let gensym___220374545: Any; + gensym___220374545 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___220374545 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + baseComponent: this, + }) + (gensym___220374545 as CustomDialogController)}); + })).backgroundColor(0x317aff); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + set __backing_hh(__backing_hh: (IStateDecoratedVariable | undefined)) + + get __backing_hh(): (IStateDecoratedVariable | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test CustomDialogController in build', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..a5ae0dbe294e454667ac3292a52fead27a1171a0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/controller-in-method.test.ts @@ -0,0 +1,175 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'controller-in-method.ets'), +]; + +const pluginTester = new PluginTester('test CutomDialogController assignment in method', buildConfig); + +const parsedTransform: Plugins = { + name: 'controller-in-method', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, CustomDialog as CustomDialog, CustomDialogController as CustomDialogController } from "@ohos.arkui.component"; +import hilog from "@ohos.hilog"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + @memo() public build() {} + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___95501822 = initializers; + (((gensym___95501822) == (null)) ? undefined : gensym___95501822.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + public updateController1() { + this.dialogController = ({let gensym___17371929: Any; + gensym___17371929 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___17371929 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + autoCancel: true, + baseComponent: this, + }) + (gensym___17371929 as CustomDialogController)}); + } + + public updateController2() { + let temp = ({let gensym___90667230: Any; + gensym___90667230 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___90667230 as CustomDialogController)); + return instance; + }), {}, undefined); + }), + autoCancel: true, + baseComponent: this, + }) + (gensym___90667230 as CustomDialogController)}); + this.dialogController = temp; + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test CutomDialogController assignment in method', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..bbbd0eb25dbfa475b8e28e00d12d14128013cf15 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/declare-custom-dialog.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { memoNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'declare-custom-dialog.ets'), +]; + +const pluginTester = new PluginTester('test declared struct @CustomDialog transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +}; + +const expectedCheckedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { State as State } from "@ohos.arkui.stateManagement"; +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, ComponentV2 as ComponentV2, Component as Component, Builder as Builder } from "@ohos.arkui.component"; + +function main() {} + +@CustomDialog() export declare final struct CustomDialogExample extends BaseCustomDialog { + public aaController?: (CustomDialogController | undefined); + + @State() public text: string; + + public hh: string; + + @Builder() @memo() public build(): void + + public constructor() {} + + public static _buildCompatibleNode(options: __Options_CustomDialogExample): void + +} + +@Component() final struct CustomDialogUserV1 extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUserV1 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___51459619 = initializers; + (((gensym___51459619) == (null)) ? undefined : gensym___51459619.dialogController)})) ?? (({let gensym___203542966: Any; + gensym___203542966 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___203542966 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + }) + (gensym___203542966 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUserV1 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct CustomDialogUserV2 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_CustomDialogUserV2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? (({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController({ + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + }) + (gensym___46528967 as CustomDialogController)}))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUserV2 | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() export declare interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + set text(text: (string | undefined)) + + get text(): (string | undefined) + set __backing_text(__backing_text: (IStateDecoratedVariable | undefined)) + + get __backing_text(): (IStateDecoratedVariable | undefined) + set hh(hh: (string | undefined)) + + get hh(): (string | undefined) + +} + +@Component() export interface __Options_CustomDialogUserV1 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} + +@ComponentV2() export interface __Options_CustomDialogUserV2 { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test declared struct @CustomDialog transformation', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); \ No newline at end of file diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7ab1a6ec4d9a1dcc3edc47a6e03ad6d0a024f48 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/custom-dialog/extends-dialog-controller.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'decorators/custom-dialog'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'extends-dialog-controller.ets'), +]; + +const pluginTester = new PluginTester('test extends class of CutomDialogController', buildConfig); + +const parsedTransform: Plugins = { + name: 'extends-dialog-controller', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, Entry as Entry, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { CustomDialog as CustomDialog, CustomDialogController as CustomDialogController, CustomDialogControllerOptions as CustomDialogControllerOptions } from "@kit.ArkUI"; + +function main() {} + +@CustomDialog() final struct CustomDialogExample extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CustomDialogExample | undefined), @memo() content: ((()=> void) | undefined)): void { + if (({let gensym___45519047 = initializers; + (((gensym___45519047) == (null)) ? undefined : gensym___45519047.aaController)})) { + this.__backing_aaController = ((({let gensym___180078470 = initializers; + (((gensym___180078470) == (null)) ? undefined : gensym___180078470.aaController)})) ?? (undefined)); + } + } + + public __updateStruct(initializers: (__Options_CustomDialogExample | undefined)): void {} + + private __backing_aaController?: (CustomDialogController | undefined); + + public get aaController(): (CustomDialogController | undefined) { + return (this.__backing_aaController as (CustomDialogController | undefined)); + } + + public set aaController(value: (CustomDialogController | undefined)) { + this.__backing_aaController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => {})); + } + + public constructor() {} + + public __setDialogController__(controller: CustomDialogController): void { + this.__backing_aaController = controller; + } +} + +class DialogControllerV2 extends CustomDialogController { + public options: CustomDialogControllerOptions; + + public constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } + +} + +class DialogControllerV3 extends DialogControllerV2 { + public options: CustomDialogControllerOptions; + + public constructor(options: CustomDialogControllerOptions) { + super(options); + this.options = options; + } + +} + +@Component() final struct CustomDialogUser extends CustomComponent { + public __initializeStruct(initializers: (__Options_CustomDialogUser | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_dialogController = ((({let gensym___176924847 = initializers; + (((gensym___176924847) == (null)) ? undefined : gensym___176924847.dialogController)})) ?? ((({let gensym___46528967: Any; + gensym___46528967 = new CustomDialogController(({ + gridCount: 4, + showInSubWindow: true, + builder: @memo() (() => { + CustomDialogExample._instantiateImpl(undefined, (() => { + const instance = new CustomDialogExample(); + instance.__setDialogController__((gensym___46528967 as CustomDialogController)); + return instance; + }), undefined, undefined); + }), + baseComponent: this, + } as CustomDialogControllerOptions)) + (gensym___46528967 as CustomDialogController)}) as DialogControllerV3))); + } + + public __updateStruct(initializers: (__Options_CustomDialogUser | undefined)): void {} + + private __backing_dialogController?: (CustomDialogController | null); + + public get dialogController(): (CustomDialogController | null) { + return (this.__backing_dialogController as (CustomDialogController | null)); + } + + public set dialogController(value: (CustomDialogController | null)) { + this.__backing_dialogController = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + if (((this.dialogController) != (null))) { + this.dialogController!.open(); + } + })); + return; + }), "click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@CustomDialog() export interface __Options_CustomDialogExample { + set aaController(aaController: ((CustomDialogController | undefined) | undefined)) + + get aaController(): ((CustomDialogController | undefined) | undefined) + +} + +@Component() export interface __Options_CustomDialogUser { + set dialogController(dialogController: ((CustomDialogController | null) | undefined)) + + get dialogController(): ((CustomDialogController | null) | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test extends class of CutomDialogController', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f726ecd68a44c8f99046d705f51033d4ed5ce62f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts @@ -0,0 +1,872 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'decorator-no-type.ets'), +]; + +const pluginTester = new PluginTester('test no typeAnnotation variables', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { BaseCustomDialog as BaseCustomDialog } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, ComponentV2 as ComponentV2, CustomDialog as CustomDialog } from "@ohos.arkui.component"; + +import { State as State, Prop as Prop, Provide as Provide, Event as Event, Local as Local, Param as Param } from "@ohos.arkui.stateManagement"; + +import { Provider as Provider, Consumer as Consumer, Once as Once, Observed as Observed, ObservedV2 as ObservedV2, Trace as Trace, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ObservedV2() class OO implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"hi"}) private __backing_hi: "150" = "150"; + + @JSONStringifyIgnore() private __meta_hi: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get hi(): "150" { + this.conditionalAddRef(this.__meta_hi); + return UIUtils.makeObserved(this.__backing_hi); + } + + public set hi(newValue: "150") { + if (((this.__backing_hi) !== (newValue))) { + this.__backing_hi = newValue; + this.__meta_hi.fireChange(); + this.executeOnSubscribingWatches("hi"); + } + } + + public constructor() {} + +} + +@Observed() class RR implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONRename({newName:"hi"}) private __backing_hi: "150" = "150"; + + @JSONStringifyIgnore() private __meta_hi: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get hi(): "150" { + this.conditionalAddRef(this.__meta_hi); + return this.__backing_hi; + } + + public set hi(newValue: "150") { + if (((this.__backing_hi) !== (newValue))) { + this.__backing_hi = newValue; + this.__meta_hi.fireChange(); + this.executeOnSubscribingWatches("hi"); + } + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", (((({let gensym___213853607 = initializers; + (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? (new Per(6))) as Per)); + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeProp>(this, "stateVar2", (((({let gensym___113574154 = initializers; + (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (new Array(3, 6, 8))) as Array)); + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar3", "stateVar3", (((({let gensym___120612294 = initializers; + (((gensym___120612294) == (null)) ? undefined : gensym___120612294.stateVar3)})) ?? (StateType.TYPE3)) as StateType), false); + this.__backing_stateVar8 = ((({let gensym___188075012 = initializers; + (((gensym___188075012) == (null)) ? undefined : gensym___188075012.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = ((({let gensym___53672736 = initializers; + (((gensym___53672736) == (null)) ? undefined : gensym___53672736.stateVar9)})) ?? (new Date("2025-4-23"))); + this.__backing_stateVar11113 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar11113", "me0", (((({let gensym___105441066 = initializers; + (((gensym___105441066) == (null)) ? undefined : gensym___105441066.stateVar11113)})) ?? (true)) as Boolean), false); + this.__backing_stateVar11114 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar11114", "stateVar11114", (((({let gensym___141950305 = initializers; + (((gensym___141950305) == (null)) ? undefined : gensym___141950305.stateVar11114)})) ?? (undefined)) as undefined), false); + this.__backing_stateVar11115 = STATE_MGMT_FACTORY.makeState(this, "stateVar11115", (((({let gensym___159362057 = initializers; + (((gensym___159362057) == (null)) ? undefined : gensym___159362057.stateVar11115)})) ?? (null)) as null)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___130780487 = initializers; + (((gensym___130780487) == (null)) ? undefined : gensym___130780487.stateVar2)})) !== (undefined))) { + this.__backing_stateVar2!.update((initializers!.stateVar2 as Array)); + } + } + + private __backing_stateVar1?: IStateDecoratedVariable; + + public get stateVar1(): Per { + return this.__backing_stateVar1!.get(); + } + + public set stateVar1(value: Per) { + this.__backing_stateVar1!.set(value); + } + + private __backing_stateVar2?: IPropDecoratedVariable>; + + public get stateVar2(): Array { + return this.__backing_stateVar2!.get(); + } + + public set stateVar2(value: Array) { + this.__backing_stateVar2!.set(value); + } + + private __backing_stateVar3?: IProvideDecoratedVariable; + + public get stateVar3(): StateType { + return this.__backing_stateVar3!.get(); + } + + public set stateVar3(value: StateType) { + this.__backing_stateVar3!.set(value); + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: Any; + + public get stateVar9(): Date { + return (this.__backing_stateVar9 as Date); + } + + public set stateVar9(value: Date) { + this.__backing_stateVar9 = value; + } + + private __backing_stateVar11113?: IProvideDecoratedVariable; + + public get stateVar11113(): Boolean { + return this.__backing_stateVar11113!.get(); + } + + public set stateVar11113(value: Boolean) { + this.__backing_stateVar11113!.set(value); + } + + private __backing_stateVar11114?: IProvideDecoratedVariable; + + public get stateVar11114(): undefined { + return this.__backing_stateVar11114!.get(); + } + + public set stateVar11114(value: undefined) { + this.__backing_stateVar11114!.set(value); + } + + private __backing_stateVar11115?: IStateDecoratedVariable; + + public get stateVar11115(): null { + return this.__backing_stateVar11115!.get(); + } + + public set stateVar11115(value: null) { + this.__backing_stateVar11115!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_V2Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "stateVar4", (((({let gensym___73118717 = initializers; + (((gensym___73118717) == (null)) ? undefined : gensym___73118717.stateVar4)})) ?? (new Set(new Array("aa", "bb")))) as Set)); + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar5", [true, false]); + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar6", new Array(new Per(7), new Per(11))); + this.__backing_stateVar7 = ((({let gensym___57869708 = initializers; + (((gensym___57869708) == (null)) ? undefined : gensym___57869708.stateVar7)})) ?? ([new Per(7), new Per(11)])); + this.__backing_stateVar8 = ((({let gensym___248714217 = initializers; + (((gensym___248714217) == (null)) ? undefined : gensym___248714217.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeParam(this, "stateVar9", (((({let gensym___78493121 = initializers; + (((gensym___78493121) == (null)) ? undefined : gensym___78493121.stateVar9)})) ?? (new Date("2025-4-23"))) as Date)); + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeParam>(this, "stateVar10", (((({let gensym___142105696 = initializers; + (((gensym___142105696) == (null)) ? undefined : gensym___142105696.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))) as Map)); + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeParamOnce(this, "stateVar11", (((({let gensym___198110537 = initializers; + (((gensym___198110537) == (null)) ? undefined : gensym___198110537.stateVar11)})) ?? (0.0)) as Double)); + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeProvider(this, "stateVar12", "stateVar12", new Per(6)); + this.__backing_stateVar11111 = STATE_MGMT_FACTORY.makeConsumer<"stateVar1">(this, "stateVar11111", "stateVar11111", "stateVar1"); + this.__backing_stateVar11188 = STATE_MGMT_FACTORY.makeProvider<"">(this, "stateVar11188", "var", ""); + this.__backing_stateVar11112 = STATE_MGMT_FACTORY.makeConsumer(this, "stateVar11112", "nihao", 50); + } + + public __updateStruct(initializers: (__Options_V2Parent | undefined)): void { + if (((({let gensym___68371156 = initializers; + (((gensym___68371156) == (null)) ? undefined : gensym___68371156.stateVar9)})) !== (undefined))) { + this.__backing_stateVar9!.update((initializers!.stateVar9 as Date)); + } + if (((({let gensym___20754573 = initializers; + (((gensym___20754573) == (null)) ? undefined : gensym___20754573.stateVar10)})) !== (undefined))) { + this.__backing_stateVar10!.update((initializers!.stateVar10 as Map)); + } + } + + private __backing_stateVar4?: IParamOnceDecoratedVariable>; + + public get stateVar4(): Set { + return this.__backing_stateVar4!.get(); + } + + public set stateVar4(value: Set) { + this.__backing_stateVar4!.set(value); + } + + private __backing_stateVar5?: ILocalDecoratedVariable>; + + public get stateVar5(): Array { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: Array) { + this.__backing_stateVar5!.set(value); + } + + private __backing_stateVar6?: ILocalDecoratedVariable>; + + public get stateVar6(): Array { + return this.__backing_stateVar6!.get(); + } + + public set stateVar6(value: Array) { + this.__backing_stateVar6!.set(value); + } + + private __backing_stateVar7?: Any; + + public get stateVar7(): Array { + return (this.__backing_stateVar7 as Array); + } + + public set stateVar7(value: Array) { + this.__backing_stateVar7 = value; + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: IParamDecoratedVariable; + + public get stateVar9(): Date { + return this.__backing_stateVar9!.get(); + } + + private __backing_stateVar10?: IParamDecoratedVariable>; + + public get stateVar10(): Map { + return this.__backing_stateVar10!.get(); + } + + private __backing_stateVar11?: IParamOnceDecoratedVariable; + + public get stateVar11(): Double { + return this.__backing_stateVar11!.get(); + } + + public set stateVar11(value: Double) { + this.__backing_stateVar11!.set(value); + } + + private __backing_stateVar12?: IProviderDecoratedVariable; + + public get stateVar12(): Per { + return this.__backing_stateVar12!.get(); + } + + public set stateVar12(value: Per) { + this.__backing_stateVar12!.set(value); + } + + private __backing_stateVar11111?: IConsumerDecoratedVariable<"stateVar1">; + + public get stateVar11111(): "stateVar1" { + return this.__backing_stateVar11111!.get(); + } + + public set stateVar11111(value: "stateVar1") { + this.__backing_stateVar11111!.set(value); + } + + private __backing_stateVar11188?: IProviderDecoratedVariable<"">; + + public get stateVar11188(): "" { + return this.__backing_stateVar11188!.get(); + } + + public set stateVar11188(value: "") { + this.__backing_stateVar11188!.set(value); + } + + private __backing_stateVar11112?: IConsumerDecoratedVariable; + + public get stateVar11112(): Int { + return this.__backing_stateVar11112!.get(); + } + + public set stateVar11112(value: Int) { + this.__backing_stateVar11112!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@CustomDialog() final struct CC extends BaseCustomDialog { + public __initializeStruct(initializers: (__Options_CC | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "stateVar4", (((({let gensym___177598336 = initializers; + (((gensym___177598336) == (null)) ? undefined : gensym___177598336.stateVar4)})) ?? (new Set(new Array("aa", "bb")))) as Set)); + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar5", [true, false]); + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "stateVar6", new Array(new Per(7), new Per(11))); + this.__backing_stateVar7 = ((({let gensym___136470640 = initializers; + (((gensym___136470640) == (null)) ? undefined : gensym___136470640.stateVar7)})) ?? ([new Per(7), new Per(11)])); + this.__backing_stateVar8 = ((({let gensym___59026893 = initializers; + (((gensym___59026893) == (null)) ? undefined : gensym___59026893.stateVar8)})) ?? (((sr: string) => {}))); + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeParam(this, "stateVar9", (((({let gensym___121624319 = initializers; + (((gensym___121624319) == (null)) ? undefined : gensym___121624319.stateVar9)})) ?? (new Date("2025-4-23"))) as Date)); + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeParam>(this, "stateVar10", (((({let gensym___40863473 = initializers; + (((gensym___40863473) == (null)) ? undefined : gensym___40863473.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))) as Map)); + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeParamOnce(this, "stateVar11", (((({let gensym___101188994 = initializers; + (((gensym___101188994) == (null)) ? undefined : gensym___101188994.stateVar11)})) ?? (0.0)) as Double)); + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeProvider(this, "stateVar12", "stateVar12", new Per(6)); + this.__backing_stateVar11111 = STATE_MGMT_FACTORY.makeConsumer<"stateVar1">(this, "stateVar11111", "stateVar11111", "stateVar1"); + this.__backing_stateVar11188 = STATE_MGMT_FACTORY.makeProvider<"">(this, "stateVar11188", "var", ""); + this.__backing_stateVar11112 = STATE_MGMT_FACTORY.makeConsumer(this, "stateVar11112", "nihao", 50); + } + + public __updateStruct(initializers: (__Options_CC | undefined)): void { + if (((({let gensym___177096870 = initializers; + (((gensym___177096870) == (null)) ? undefined : gensym___177096870.stateVar9)})) !== (undefined))) { + this.__backing_stateVar9!.update((initializers!.stateVar9 as Date)); + } + if (((({let gensym___35982320 = initializers; + (((gensym___35982320) == (null)) ? undefined : gensym___35982320.stateVar10)})) !== (undefined))) { + this.__backing_stateVar10!.update((initializers!.stateVar10 as Map)); + } + } + + private __backing_stateVar4?: IParamOnceDecoratedVariable>; + + public get stateVar4(): Set { + return this.__backing_stateVar4!.get(); + } + + public set stateVar4(value: Set) { + this.__backing_stateVar4!.set(value); + } + + private __backing_stateVar5?: ILocalDecoratedVariable>; + + public get stateVar5(): Array { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: Array) { + this.__backing_stateVar5!.set(value); + } + + private __backing_stateVar6?: ILocalDecoratedVariable>; + + public get stateVar6(): Array { + return this.__backing_stateVar6!.get(); + } + + public set stateVar6(value: Array) { + this.__backing_stateVar6!.set(value); + } + + private __backing_stateVar7?: Any; + + public get stateVar7(): Array { + return (this.__backing_stateVar7 as Array); + } + + public set stateVar7(value: Array) { + this.__backing_stateVar7 = value; + } + + private __backing_stateVar8?: Any; + + public get stateVar8(): (sr: String) => void { + return (this.__backing_stateVar8 as (sr: String) => void); + } + + public set stateVar8(value: (sr: String) => void) { + this.__backing_stateVar8 = value; + } + + private __backing_stateVar9?: IParamDecoratedVariable; + + public get stateVar9(): Date { + return this.__backing_stateVar9!.get(); + } + + private __backing_stateVar10?: IParamDecoratedVariable>; + + public get stateVar10(): Map { + return this.__backing_stateVar10!.get(); + } + + private __backing_stateVar11?: IParamOnceDecoratedVariable; + + public get stateVar11(): Double { + return this.__backing_stateVar11!.get(); + } + + public set stateVar11(value: Double) { + this.__backing_stateVar11!.set(value); + } + + private __backing_stateVar12?: IProviderDecoratedVariable; + + public get stateVar12(): Per { + return this.__backing_stateVar12!.get(); + } + + public set stateVar12(value: Per) { + this.__backing_stateVar12!.set(value); + } + + private __backing_stateVar11111?: IConsumerDecoratedVariable<"stateVar1">; + + public get stateVar11111(): "stateVar1" { + return this.__backing_stateVar11111!.get(); + } + + public set stateVar11111(value: "stateVar1") { + this.__backing_stateVar11111!.set(value); + } + + private __backing_stateVar11188?: IProviderDecoratedVariable<"">; + + public get stateVar11188(): "" { + return this.__backing_stateVar11188!.get(); + } + + public set stateVar11188(value: "") { + this.__backing_stateVar11188!.set(value); + } + + private __backing_stateVar11112?: IConsumerDecoratedVariable; + + public get stateVar11112(): Int { + return this.__backing_stateVar11112!.get(); + } + + public set stateVar11112(value: Int) { + this.__backing_stateVar11112!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (Any | undefined)) + + get stateVar1(): (Any | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (Any | undefined)) + + get stateVar2(): (Any | undefined) + set __backing_stateVar2(__backing_stateVar2: (IPropDecoratedVariable | undefined)) + + get __backing_stateVar2(): (IPropDecoratedVariable | undefined) + set stateVar3(stateVar3: (Any | undefined)) + + get stateVar3(): (Any | undefined) + set __backing_stateVar3(__backing_stateVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IProvideDecoratedVariable | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set stateVar11113(stateVar11113: (Any | undefined)) + + get stateVar11113(): (Any | undefined) + set __backing_stateVar11113(__backing_stateVar11113: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar11113(): (IProvideDecoratedVariable | undefined) + set stateVar11114(stateVar11114: (Any | undefined)) + + get stateVar11114(): (Any | undefined) + set __backing_stateVar11114(__backing_stateVar11114: (IProvideDecoratedVariable | undefined)) + + get __backing_stateVar11114(): (IProvideDecoratedVariable | undefined) + set stateVar11115(stateVar11115: (Any | undefined)) + + get stateVar11115(): (Any | undefined) + set __backing_stateVar11115(__backing_stateVar11115: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar11115(): (IStateDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_V2Parent { + set stateVar4(stateVar4: (Any | undefined)) + + get stateVar4(): (Any | undefined) + @Param() set __backing_stateVar4(__backing_stateVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar4(): (IParamOnceDecoratedVariable | undefined) + set stateVar5(stateVar5: (Any | undefined)) + + get stateVar5(): (Any | undefined) + set __backing_stateVar5(__backing_stateVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar5(): (ILocalDecoratedVariable | undefined) + set stateVar6(stateVar6: (Any | undefined)) + + get stateVar6(): (Any | undefined) + set __backing_stateVar6(__backing_stateVar6: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar6(): (ILocalDecoratedVariable | undefined) + set stateVar7(stateVar7: (Any | undefined)) + + get stateVar7(): (Any | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set __backing_stateVar9(__backing_stateVar9: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IParamDecoratedVariable | undefined) + set stateVar10(stateVar10: (Any | undefined)) + + get stateVar10(): (Any | undefined) + set __backing_stateVar10(__backing_stateVar10: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar10(): (IParamDecoratedVariable | undefined) + set stateVar11(stateVar11: (Any | undefined)) + + get stateVar11(): (Any | undefined) + @Param() set __backing_stateVar11(__backing_stateVar11: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar11(): (IParamOnceDecoratedVariable | undefined) + set stateVar12(stateVar12: (Any | undefined)) + + get stateVar12(): (Any | undefined) + set __backing_stateVar12(__backing_stateVar12: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar12(): (IProviderDecoratedVariable | undefined) + set stateVar11111(stateVar11111: (Any | undefined)) + + get stateVar11111(): (Any | undefined) + set __backing_stateVar11111(__backing_stateVar11111: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11111(): (IConsumerDecoratedVariable | undefined) + set stateVar11188(stateVar11188: (Any | undefined)) + + get stateVar11188(): (Any | undefined) + set __backing_stateVar11188(__backing_stateVar11188: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar11188(): (IProviderDecoratedVariable | undefined) + set stateVar11112(stateVar11112: (Any | undefined)) + + get stateVar11112(): (Any | undefined) + set __backing_stateVar11112(__backing_stateVar11112: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11112(): (IConsumerDecoratedVariable | undefined) + +} + +@CustomDialog() export interface __Options_CC { + set stateVar4(stateVar4: (Any | undefined)) + + get stateVar4(): (Any | undefined) + @Param() set __backing_stateVar4(__backing_stateVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar4(): (IParamOnceDecoratedVariable | undefined) + set stateVar5(stateVar5: (Any | undefined)) + + get stateVar5(): (Any | undefined) + set __backing_stateVar5(__backing_stateVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar5(): (ILocalDecoratedVariable | undefined) + set stateVar6(stateVar6: (Any | undefined)) + + get stateVar6(): (Any | undefined) + set __backing_stateVar6(__backing_stateVar6: (ILocalDecoratedVariable | undefined)) + + get __backing_stateVar6(): (ILocalDecoratedVariable | undefined) + set stateVar7(stateVar7: (Any | undefined)) + + get stateVar7(): (Any | undefined) + set stateVar8(stateVar8: (Any | undefined)) + + get stateVar8(): (Any | undefined) + set stateVar9(stateVar9: (Any | undefined)) + + get stateVar9(): (Any | undefined) + set __backing_stateVar9(__backing_stateVar9: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IParamDecoratedVariable | undefined) + set stateVar10(stateVar10: (Any | undefined)) + + get stateVar10(): (Any | undefined) + set __backing_stateVar10(__backing_stateVar10: (IParamDecoratedVariable | undefined)) + + get __backing_stateVar10(): (IParamDecoratedVariable | undefined) + set stateVar11(stateVar11: (Any | undefined)) + + get stateVar11(): (Any | undefined) + @Param() set __backing_stateVar11(__backing_stateVar11: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_stateVar11(): (IParamOnceDecoratedVariable | undefined) + set stateVar12(stateVar12: (Any | undefined)) + + get stateVar12(): (Any | undefined) + set __backing_stateVar12(__backing_stateVar12: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar12(): (IProviderDecoratedVariable | undefined) + set stateVar11111(stateVar11111: (Any | undefined)) + + get stateVar11111(): (Any | undefined) + set __backing_stateVar11111(__backing_stateVar11111: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11111(): (IConsumerDecoratedVariable | undefined) + set stateVar11188(stateVar11188: (Any | undefined)) + + get stateVar11188(): (Any | undefined) + set __backing_stateVar11188(__backing_stateVar11188: (IProviderDecoratedVariable | undefined)) + + get __backing_stateVar11188(): (IProviderDecoratedVariable | undefined) + set stateVar11112(stateVar11112: (Any | undefined)) + + get stateVar11112(): (Any | undefined) + set __backing_stateVar11112(__backing_stateVar11112: (IConsumerDecoratedVariable | undefined)) + + get __backing_stateVar11112(): (IConsumerDecoratedVariable | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test no typeAnnotation variables', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5e8c36a3612696f90f207bc94d554a04ffbdf4b --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/event/event-initialize.test.ts @@ -0,0 +1,281 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/event'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'event-initialize.ets'), +]; + +const pluginTester = new PluginTester('test @Event decorator transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Event as Event, Param as Param, Local as Local } from "@ohos.arkui.stateManagement"; + +@ComponentV2() final struct Child extends CustomComponentV2 { + @Param() public index: number = 0; + + @Event() public changeIndex!: ((val: number)=> void); + + @Event() public testEvent!: ((val: number)=> number); + + @Event() public testEvent2: ((val: number)=> number) = ((val: number) => { + return val; + }); + + public build() { + Column(){ + Text(\`Child index: \${this.index}\`).onClick(((e) => { + this.changeIndex(20); + console.log(\`after changeIndex \${this.index}\`); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public index: number = 0; + + public build() { + Column(){ + Child({ + index: this.index, + changeIndex: ((val: number) => { + this.index = val; + console.log(\`in changeIndex \${this.index}\`); + }), + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + index?: number; + @Param() __backing_index?: number; + changeIndex?: ((val: number)=> void); + testEvent?: ((val: number)=> number); + testEvent2?: ((val: number)=> number); + +} + +@ComponentV2() export interface __Options_Index { + index?: number; + @Local() __backing_index?: number; + +} +`; + +const expectedCheckedScript: string = ` +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Event as Event, Param as Param, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_index = STATE_MGMT_FACTORY.makeParam(this, "index", ((({let gensym___23942905 = initializers; + (((gensym___23942905) == (null)) ? undefined : gensym___23942905.index)})) ?? (0))); + this.__backing_changeIndex = ((({let gensym___204042774 = initializers; + (((gensym___204042774) == (null)) ? undefined : gensym___204042774.changeIndex)})) ?? (undefined)); + this.__backing_testEvent = ((({let gensym___124585092 = initializers; + (((gensym___124585092) == (null)) ? undefined : gensym___124585092.testEvent)})) ?? (undefined)); + this.__backing_testEvent2 = ((({let gensym___189097286 = initializers; + (((gensym___189097286) == (null)) ? undefined : gensym___189097286.testEvent2)})) ?? (((val: number) => { + return val; + }))); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___91647805 = initializers; + (((gensym___91647805) == (null)) ? undefined : gensym___91647805.index)})) !== (undefined))) { + this.__backing_index!.update((initializers!.index as number)); + } + } + + private __backing_index?: IParamDecoratedVariable; + + public get index(): number { + return this.__backing_index!.get(); + } + + private __backing_changeIndex?: ((val: number)=> void); + + public get changeIndex(): ((val: number)=> void) { + return (this.__backing_changeIndex as ((val: number)=> void)); + } + + public set changeIndex(value: ((val: number)=> void)) { + this.__backing_changeIndex = value; + } + + private __backing_testEvent?: ((val: number)=> number); + + public get testEvent(): ((val: number)=> number) { + return (this.__backing_testEvent as ((val: number)=> number)); + } + + public set testEvent(value: ((val: number)=> number)) { + this.__backing_testEvent = value; + } + + private __backing_testEvent2?: ((val: number)=> number); + + public get testEvent2(): ((val: number)=> number) { + return (this.__backing_testEvent2 as ((val: number)=> number)); + } + + public set testEvent2(value: ((val: number)=> number)) { + this.__backing_testEvent2 = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.onClick(((e) => { + this.changeIndex(20); + console.log(\`after changeIndex \${this.index}\`); + })); + return; + }), \`Child index: \${this.index}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_index = STATE_MGMT_FACTORY.makeLocal(this, "index", 0); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_index?: ILocalDecoratedVariable; + + public get index(): number { + return this.__backing_index!.get(); + } + + public set index(value: number) { + this.__backing_index!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + index: this.index, + changeIndex: ((val: number) => { + this.index = val; + console.log(\`in changeIndex \${this.index}\`); + }), + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set index(index: (number | undefined)) + + get index(): (number | undefined) + set __backing_index(__backing_index: (IParamDecoratedVariable | undefined)) + + get __backing_index(): (IParamDecoratedVariable | undefined) + set changeIndex(changeIndex: (((val: number)=> void) | undefined)) + + get changeIndex(): (((val: number)=> void) | undefined) + set testEvent(testEvent: (((val: number)=> number) | undefined)) + + get testEvent(): (((val: number)=> number) | undefined) + set testEvent2(testEvent2: (((val: number)=> number) | undefined)) + + get testEvent2(): (((val: number)=> number) | undefined) + +} + +@ComponentV2() export interface __Options_Index { + set index(index: (number | undefined)) + + get index(): (number | undefined) + set __backing_index(__backing_index: (ILocalDecoratedVariable | undefined)) + + get __backing_index(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Event decorator transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts index 340db6a9eacac05ec71919e9af39810b6c26c7fc..2ce87652911deab846655a760e2f754395687344 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,101 +38,142 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Link as Link } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class LinkParent extends CustomComponent { - public __initializeStruct(initializers: __Options_LinkParent | undefined, @memo() content: (()=> void) | undefined): void { + + +@Component() final struct LinkParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_LinkParent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___11910109 = initializers; (((gensym___11910109) == (null)) ? undefined : gensym___11910109.__backing_linkVar1)})) { - this.__backing_linkVar1 = new LinkDecoratedVariable("linkVar1", initializers!.__backing_linkVar1!); + this.__backing_linkVar1 = STATE_MGMT_FACTORY.makeLink(this, "linkVar1", initializers!.__backing_linkVar1!); }; if (({let gensym___181684045 = initializers; (((gensym___181684045) == (null)) ? undefined : gensym___181684045.__backing_linkVar2)})) { - this.__backing_linkVar2 = new LinkDecoratedVariable("linkVar2", initializers!.__backing_linkVar2!); + this.__backing_linkVar2 = STATE_MGMT_FACTORY.makeLink(this, "linkVar2", initializers!.__backing_linkVar2!); }; if (({let gensym___24446313 = initializers; (((gensym___24446313) == (null)) ? undefined : gensym___24446313.__backing_linkVar3)})) { - this.__backing_linkVar3 = new LinkDecoratedVariable("linkVar3", initializers!.__backing_linkVar3!); + this.__backing_linkVar3 = STATE_MGMT_FACTORY.makeLink(this, "linkVar3", initializers!.__backing_linkVar3!); }; if (({let gensym___167989826 = initializers; (((gensym___167989826) == (null)) ? undefined : gensym___167989826.__backing_linkVar4)})) { - this.__backing_linkVar4 = new LinkDecoratedVariable("linkVar4", initializers!.__backing_linkVar4!); + this.__backing_linkVar4 = STATE_MGMT_FACTORY.makeLink(this, "linkVar4", initializers!.__backing_linkVar4!); }; if (({let gensym___157566097 = initializers; (((gensym___157566097) == (null)) ? undefined : gensym___157566097.__backing_linkVar5)})) { - this.__backing_linkVar5 = new LinkDecoratedVariable("linkVar5", initializers!.__backing_linkVar5!); + this.__backing_linkVar5 = STATE_MGMT_FACTORY.makeLink(this, "linkVar5", initializers!.__backing_linkVar5!); }; } - public __updateStruct(initializers: __Options_LinkParent | undefined): void {} - private __backing_linkVar1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_LinkParent | undefined)): void {} + + private __backing_linkVar1?: ILinkDecoratedVariable; + public get linkVar1(): string { return this.__backing_linkVar1!.get(); } + public set linkVar1(value: string) { this.__backing_linkVar1!.set(value); } - private __backing_linkVar2?: LinkDecoratedVariable; + + private __backing_linkVar2?: ILinkDecoratedVariable; + public get linkVar2(): number { return this.__backing_linkVar2!.get(); } + public set linkVar2(value: number) { this.__backing_linkVar2!.set(value); } - private __backing_linkVar3?: LinkDecoratedVariable; + + private __backing_linkVar3?: ILinkDecoratedVariable; + public get linkVar3(): boolean { return this.__backing_linkVar3!.get(); } + public set linkVar3(value: boolean) { this.__backing_linkVar3!.set(value); } - private __backing_linkVar4?: LinkDecoratedVariable; + + private __backing_linkVar4?: ILinkDecoratedVariable; + public get linkVar4(): undefined { return this.__backing_linkVar4!.get(); } + public set linkVar4(value: undefined) { this.__backing_linkVar4!.set(value); } - private __backing_linkVar5?: LinkDecoratedVariable; + + private __backing_linkVar5?: ILinkDecoratedVariable; + public get linkVar5(): null { return this.__backing_linkVar5!.get(); } + public set linkVar5(value: null) { this.__backing_linkVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: LinkParent)=> LinkParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_LinkParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_LinkParent { - set linkVar1(linkVar1: string | undefined) - get linkVar1(): string | undefined - set __backing_linkVar1(__backing_linkVar1: DecoratedV1VariableBase | undefined) - get __backing_linkVar1(): DecoratedV1VariableBase | undefined - set linkVar2(linkVar2: number | undefined) - get linkVar2(): number | undefined - set __backing_linkVar2(__backing_linkVar2: DecoratedV1VariableBase | undefined) - get __backing_linkVar2(): DecoratedV1VariableBase | undefined - set linkVar3(linkVar3: boolean | undefined) - get linkVar3(): boolean | undefined - set __backing_linkVar3(__backing_linkVar3: DecoratedV1VariableBase | undefined) - get __backing_linkVar3(): DecoratedV1VariableBase | undefined - set linkVar4(linkVar4: undefined | undefined) - get linkVar4(): undefined | undefined - set __backing_linkVar4(__backing_linkVar4: DecoratedV1VariableBase | undefined) - get __backing_linkVar4(): DecoratedV1VariableBase | undefined - set linkVar5(linkVar5: null | undefined) - get linkVar5(): null | undefined - set __backing_linkVar5(__backing_linkVar5: DecoratedV1VariableBase | undefined) - get __backing_linkVar5(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_LinkParent { + @__Link_intrinsic() set linkVar1(linkVar1: (string | undefined)) + + @__Link_intrinsic() get linkVar1(): (string | undefined) + set __backing_linkVar1(__backing_linkVar1: (LinkSourceType | undefined)) + + get __backing_linkVar1(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar2(linkVar2: (number | undefined)) + + @__Link_intrinsic() get linkVar2(): (number | undefined) + set __backing_linkVar2(__backing_linkVar2: (LinkSourceType | undefined)) + + get __backing_linkVar2(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar3(linkVar3: (boolean | undefined)) + + @__Link_intrinsic() get linkVar3(): (boolean | undefined) + set __backing_linkVar3(__backing_linkVar3: (LinkSourceType | undefined)) + + get __backing_linkVar3(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar4(linkVar4: (undefined | undefined)) + + @__Link_intrinsic() get linkVar4(): (undefined | undefined) + set __backing_linkVar4(__backing_linkVar4: (LinkSourceType | undefined)) + + get __backing_linkVar4(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar5(linkVar5: (null | undefined)) + + @__Link_intrinsic() get linkVar5(): (null | undefined) + set __backing_linkVar5(__backing_linkVar5: (LinkSourceType | undefined)) + + get __backing_linkVar5(): (LinkSourceType | undefined) + } `; @@ -141,9 +183,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Link decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts index 486097a84761d4f03315d22735a709bfe1b4e7be..f174a08a5e213ea3cc0616f62777199123fbcfac 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,41 +38,60 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Link as Link } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class LinkType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: LinkType = new LinkType(0, 0); + public static readonly TYPE2: LinkType = new LinkType(1, 1); + public static readonly TYPE3: LinkType = new LinkType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: LinkType[] = [LinkType.TYPE1, LinkType.TYPE2, LinkType.TYPE3]; + public getName(): String { return LinkType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): LinkType { for (let i = 0;((i) < (LinkType.#NamesArray.length));(++i)) { if (((name) == (LinkType.#NamesArray[i]))) { @@ -89,212 +109,285 @@ final class LinkType extends BaseEnum { } throw new Error((("No enum LinkType with value ") + (value))); } + public valueOf(): int { return LinkType.#ValuesArray[this.#ordinal]; } + public toString(): String { return LinkType.#StringValuesArray[this.#ordinal]; } + public static values(): LinkType[] { return LinkType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: LinkType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___11910109 = initializers; (((gensym___11910109) == (null)) ? undefined : gensym___11910109.__backing_linkVar1)})) { - this.__backing_linkVar1 = new LinkDecoratedVariable("linkVar1", initializers!.__backing_linkVar1!); + this.__backing_linkVar1 = STATE_MGMT_FACTORY.makeLink(this, "linkVar1", initializers!.__backing_linkVar1!); }; if (({let gensym___181684045 = initializers; (((gensym___181684045) == (null)) ? undefined : gensym___181684045.__backing_linkVar2)})) { - this.__backing_linkVar2 = new LinkDecoratedVariable>("linkVar2", initializers!.__backing_linkVar2!); + this.__backing_linkVar2 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar2", initializers!.__backing_linkVar2!); }; if (({let gensym___24446313 = initializers; (((gensym___24446313) == (null)) ? undefined : gensym___24446313.__backing_linkVar3)})) { - this.__backing_linkVar3 = new LinkDecoratedVariable("linkVar3", initializers!.__backing_linkVar3!); + this.__backing_linkVar3 = STATE_MGMT_FACTORY.makeLink(this, "linkVar3", initializers!.__backing_linkVar3!); }; if (({let gensym___167989826 = initializers; (((gensym___167989826) == (null)) ? undefined : gensym___167989826.__backing_linkVar4)})) { - this.__backing_linkVar4 = new LinkDecoratedVariable>("linkVar4", initializers!.__backing_linkVar4!); + this.__backing_linkVar4 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar4", initializers!.__backing_linkVar4!); }; if (({let gensym___157566097 = initializers; (((gensym___157566097) == (null)) ? undefined : gensym___157566097.__backing_linkVar5)})) { - this.__backing_linkVar5 = new LinkDecoratedVariable>("linkVar5", initializers!.__backing_linkVar5!); + this.__backing_linkVar5 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar5", initializers!.__backing_linkVar5!); }; if (({let gensym___60105491 = initializers; (((gensym___60105491) == (null)) ? undefined : gensym___60105491.__backing_linkVar6)})) { - this.__backing_linkVar6 = new LinkDecoratedVariable>("linkVar6", initializers!.__backing_linkVar6!); + this.__backing_linkVar6 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar6", initializers!.__backing_linkVar6!); }; if (({let gensym___3429048 = initializers; (((gensym___3429048) == (null)) ? undefined : gensym___3429048.__backing_linkVar7)})) { - this.__backing_linkVar7 = new LinkDecoratedVariable>("linkVar7", initializers!.__backing_linkVar7!); + this.__backing_linkVar7 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar7", initializers!.__backing_linkVar7!); }; if (({let gensym___139916435 = initializers; (((gensym___139916435) == (null)) ? undefined : gensym___139916435.__backing_linkVar8)})) { - this.__backing_linkVar8 = new LinkDecoratedVariable<((sr: string)=> void)>("linkVar8", initializers!.__backing_linkVar8!); + this.__backing_linkVar8 = STATE_MGMT_FACTORY.makeLink<((sr: string)=> void)>(this, "linkVar8", initializers!.__backing_linkVar8!); }; if (({let gensym___145003260 = initializers; (((gensym___145003260) == (null)) ? undefined : gensym___145003260.__backing_linkVar9)})) { - this.__backing_linkVar9 = new LinkDecoratedVariable("linkVar9", initializers!.__backing_linkVar9!); + this.__backing_linkVar9 = STATE_MGMT_FACTORY.makeLink(this, "linkVar9", initializers!.__backing_linkVar9!); }; if (({let gensym___122643185 = initializers; (((gensym___122643185) == (null)) ? undefined : gensym___122643185.__backing_linkVar10)})) { - this.__backing_linkVar10 = new LinkDecoratedVariable>("linkVar10", initializers!.__backing_linkVar10!); + this.__backing_linkVar10 = STATE_MGMT_FACTORY.makeLink>(this, "linkVar10", initializers!.__backing_linkVar10!); }; if (({let gensym___222468503 = initializers; (((gensym___222468503) == (null)) ? undefined : gensym___222468503.__backing_linkVar11)})) { - this.__backing_linkVar11 = new LinkDecoratedVariable("linkVar11", initializers!.__backing_linkVar11!); + this.__backing_linkVar11 = STATE_MGMT_FACTORY.makeLink<(string | number)>(this, "linkVar11", initializers!.__backing_linkVar11!); }; if (({let gensym___243301539 = initializers; (((gensym___243301539) == (null)) ? undefined : gensym___243301539.__backing_linkVar12)})) { - this.__backing_linkVar12 = new LinkDecoratedVariable | Per>("linkVar12", initializers!.__backing_linkVar12!); + this.__backing_linkVar12 = STATE_MGMT_FACTORY.makeLink<(Set | Per)>(this, "linkVar12", initializers!.__backing_linkVar12!); }; } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_linkVar1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_linkVar1?: ILinkDecoratedVariable; + public get linkVar1(): Per { return this.__backing_linkVar1!.get(); } + public set linkVar1(value: Per) { this.__backing_linkVar1!.set(value); } - private __backing_linkVar2?: LinkDecoratedVariable>; + + private __backing_linkVar2?: ILinkDecoratedVariable>; + public get linkVar2(): Array { return this.__backing_linkVar2!.get(); } + public set linkVar2(value: Array) { this.__backing_linkVar2!.set(value); } - private __backing_linkVar3?: LinkDecoratedVariable; + + private __backing_linkVar3?: ILinkDecoratedVariable; + public get linkVar3(): LinkType { return this.__backing_linkVar3!.get(); } + public set linkVar3(value: LinkType) { this.__backing_linkVar3!.set(value); } - private __backing_linkVar4?: LinkDecoratedVariable>; + + private __backing_linkVar4?: ILinkDecoratedVariable>; + public get linkVar4(): Set { return this.__backing_linkVar4!.get(); } + public set linkVar4(value: Set) { this.__backing_linkVar4!.set(value); } - private __backing_linkVar5?: LinkDecoratedVariable>; + + private __backing_linkVar5?: ILinkDecoratedVariable>; + public get linkVar5(): Array { return this.__backing_linkVar5!.get(); } + public set linkVar5(value: Array) { this.__backing_linkVar5!.set(value); } - private __backing_linkVar6?: LinkDecoratedVariable>; + + private __backing_linkVar6?: ILinkDecoratedVariable>; + public get linkVar6(): Array { return this.__backing_linkVar6!.get(); } + public set linkVar6(value: Array) { this.__backing_linkVar6!.set(value); } - private __backing_linkVar7?: LinkDecoratedVariable>; + + private __backing_linkVar7?: ILinkDecoratedVariable>; + public get linkVar7(): Array { return this.__backing_linkVar7!.get(); } + public set linkVar7(value: Array) { this.__backing_linkVar7!.set(value); } - private __backing_linkVar8?: LinkDecoratedVariable<((sr: string)=> void)>; + + private __backing_linkVar8?: ILinkDecoratedVariable<((sr: string)=> void)>; + public get linkVar8(): ((sr: string)=> void) { return this.__backing_linkVar8!.get(); } + public set linkVar8(value: ((sr: string)=> void)) { this.__backing_linkVar8!.set(value); } - private __backing_linkVar9?: LinkDecoratedVariable; + + private __backing_linkVar9?: ILinkDecoratedVariable; + public get linkVar9(): Date { return this.__backing_linkVar9!.get(); } + public set linkVar9(value: Date) { this.__backing_linkVar9!.set(value); } - private __backing_linkVar10?: LinkDecoratedVariable>; + + private __backing_linkVar10?: ILinkDecoratedVariable>; + public get linkVar10(): Map { return this.__backing_linkVar10!.get(); } + public set linkVar10(value: Map) { this.__backing_linkVar10!.set(value); } - private __backing_linkVar11?: LinkDecoratedVariable; - public get linkVar11(): string | number { + + private __backing_linkVar11?: ILinkDecoratedVariable<(string | number)>; + + public get linkVar11(): (string | number) { return this.__backing_linkVar11!.get(); } - public set linkVar11(value: string | number) { + + public set linkVar11(value: (string | number)) { this.__backing_linkVar11!.set(value); } - private __backing_linkVar12?: LinkDecoratedVariable | Per>; - public get linkVar12(): Set | Per { + + private __backing_linkVar12?: ILinkDecoratedVariable<(Set | Per)>; + + public get linkVar12(): (Set | Per) { return this.__backing_linkVar12!.get(); } - public set linkVar12(value: Set | Per) { + + public set linkVar12(value: (Set | Per)) { this.__backing_linkVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set linkVar1(linkVar1: Per | undefined) - get linkVar1(): Per | undefined - set __backing_linkVar1(__backing_linkVar1: DecoratedV1VariableBase | undefined) - get __backing_linkVar1(): DecoratedV1VariableBase | undefined - set linkVar2(linkVar2: Array | undefined) - get linkVar2(): Array | undefined - set __backing_linkVar2(__backing_linkVar2: DecoratedV1VariableBase> | undefined) - get __backing_linkVar2(): DecoratedV1VariableBase> | undefined - set linkVar3(linkVar3: LinkType | undefined) - get linkVar3(): LinkType | undefined - set __backing_linkVar3(__backing_linkVar3: DecoratedV1VariableBase | undefined) - get __backing_linkVar3(): DecoratedV1VariableBase | undefined - set linkVar4(linkVar4: Set | undefined) - get linkVar4(): Set | undefined - set __backing_linkVar4(__backing_linkVar4: DecoratedV1VariableBase> | undefined) - get __backing_linkVar4(): DecoratedV1VariableBase> | undefined - set linkVar5(linkVar5: Array | undefined) - get linkVar5(): Array | undefined - set __backing_linkVar5(__backing_linkVar5: DecoratedV1VariableBase> | undefined) - get __backing_linkVar5(): DecoratedV1VariableBase> | undefined - set linkVar6(linkVar6: Array | undefined) - get linkVar6(): Array | undefined - set __backing_linkVar6(__backing_linkVar6: DecoratedV1VariableBase> | undefined) - get __backing_linkVar6(): DecoratedV1VariableBase> | undefined - set linkVar7(linkVar7: Array | undefined) - get linkVar7(): Array | undefined - set __backing_linkVar7(__backing_linkVar7: DecoratedV1VariableBase> | undefined) - get __backing_linkVar7(): DecoratedV1VariableBase> | undefined - set linkVar8(linkVar8: ((sr: string)=> void) | undefined) - get linkVar8(): ((sr: string)=> void) | undefined - set __backing_linkVar8(__backing_linkVar8: DecoratedV1VariableBase<((sr: string)=> void)> | undefined) - get __backing_linkVar8(): DecoratedV1VariableBase<((sr: string)=> void)> | undefined - set linkVar9(linkVar9: Date | undefined) - get linkVar9(): Date | undefined - set __backing_linkVar9(__backing_linkVar9: DecoratedV1VariableBase | undefined) - get __backing_linkVar9(): DecoratedV1VariableBase | undefined - set linkVar10(linkVar10: Map | undefined) - get linkVar10(): Map | undefined - set __backing_linkVar10(__backing_linkVar10: DecoratedV1VariableBase> | undefined) - get __backing_linkVar10(): DecoratedV1VariableBase> | undefined - set linkVar11(linkVar11: string | number | undefined) - get linkVar11(): string | number | undefined - set __backing_linkVar11(__backing_linkVar11: DecoratedV1VariableBase | undefined) - get __backing_linkVar11(): DecoratedV1VariableBase | undefined - set linkVar12(linkVar12: Set | Per | undefined) - get linkVar12(): Set | Per | undefined - set __backing_linkVar12(__backing_linkVar12: DecoratedV1VariableBase | Per> | undefined) - get __backing_linkVar12(): DecoratedV1VariableBase | Per> | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_Parent { + @__Link_intrinsic() set linkVar1(linkVar1: (Per | undefined)) + + @__Link_intrinsic() get linkVar1(): (Per | undefined) + set __backing_linkVar1(__backing_linkVar1: (LinkSourceType | undefined)) + + get __backing_linkVar1(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar2(linkVar2: (Array | undefined)) + + @__Link_intrinsic() get linkVar2(): (Array | undefined) + set __backing_linkVar2(__backing_linkVar2: (LinkSourceType> | undefined)) + + get __backing_linkVar2(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar3(linkVar3: (LinkType | undefined)) + + @__Link_intrinsic() get linkVar3(): (LinkType | undefined) + set __backing_linkVar3(__backing_linkVar3: (LinkSourceType | undefined)) + + get __backing_linkVar3(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar4(linkVar4: (Set | undefined)) + + @__Link_intrinsic() get linkVar4(): (Set | undefined) + set __backing_linkVar4(__backing_linkVar4: (LinkSourceType> | undefined)) + + get __backing_linkVar4(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar5(linkVar5: (Array | undefined)) + + @__Link_intrinsic() get linkVar5(): (Array | undefined) + set __backing_linkVar5(__backing_linkVar5: (LinkSourceType> | undefined)) + + get __backing_linkVar5(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar6(linkVar6: (Array | undefined)) + + @__Link_intrinsic() get linkVar6(): (Array | undefined) + set __backing_linkVar6(__backing_linkVar6: (LinkSourceType> | undefined)) + + get __backing_linkVar6(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar7(linkVar7: (Array | undefined)) + + @__Link_intrinsic() get linkVar7(): (Array | undefined) + set __backing_linkVar7(__backing_linkVar7: (LinkSourceType> | undefined)) + + get __backing_linkVar7(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar8(linkVar8: (((sr: string)=> void) | undefined)) + + @__Link_intrinsic() get linkVar8(): (((sr: string)=> void) | undefined) + set __backing_linkVar8(__backing_linkVar8: (LinkSourceType<((sr: string)=> void)> | undefined)) + + get __backing_linkVar8(): (LinkSourceType<((sr: string)=> void)> | undefined) + @__Link_intrinsic() set linkVar9(linkVar9: (Date | undefined)) + + @__Link_intrinsic() get linkVar9(): (Date | undefined) + set __backing_linkVar9(__backing_linkVar9: (LinkSourceType | undefined)) + + get __backing_linkVar9(): (LinkSourceType | undefined) + @__Link_intrinsic() set linkVar10(linkVar10: (Map | undefined)) + + @__Link_intrinsic() get linkVar10(): (Map | undefined) + set __backing_linkVar10(__backing_linkVar10: (LinkSourceType> | undefined)) + + get __backing_linkVar10(): (LinkSourceType> | undefined) + @__Link_intrinsic() set linkVar11(linkVar11: ((string | number) | undefined)) + + @__Link_intrinsic() get linkVar11(): ((string | number) | undefined) + set __backing_linkVar11(__backing_linkVar11: (LinkSourceType<(string | number)> | undefined)) + + get __backing_linkVar11(): (LinkSourceType<(string | number)> | undefined) + @__Link_intrinsic() set linkVar12(linkVar12: ((Set | Per) | undefined)) + + @__Link_intrinsic() get linkVar12(): ((Set | Per) | undefined) + set __backing_linkVar12(__backing_linkVar12: (LinkSourceType<(Set | Per)> | undefined)) + + get __backing_linkVar12(): (LinkSourceType<(Set | Per)> | undefined) + } `; @@ -304,9 +397,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Link decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts index 01a8275f91b30ba6ba4f45a333f48b0424eae2d6..88ca21cd4824894ffa11c226ae8c46492649898c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,67 +38,81 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UITextInputAttribute as UITextInputAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, TextInput as TextInput } from "@ohos.arkui.component"; + import { Link as Link, State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Parant extends CustomComponent { - public __initializeStruct(initializers: __Options_Parant | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Parant extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parant | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___10127521 = initializers; (((gensym___10127521) == (null)) ? undefined : gensym___10127521.__backing_text1)})) { - this.__backing_text1 = new LinkDecoratedVariable("text1", initializers!.__backing_text1!); + this.__backing_text1 = STATE_MGMT_FACTORY.makeLink(this, "text1", initializers!.__backing_text1!); }; } - public __updateStruct(initializers: __Options_Parant | undefined): void {} - private __backing_text1?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parant | undefined)): void {} + + private __backing_text1?: ILinkDecoratedVariable; + public get text1(): string { return this.__backing_text1!.get(); } + public set text1(value: string) { this.__backing_text1!.set(value); } - @memo() public _build(@memo() style: ((instance: Parant)=> Parant) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parant | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { TextInput(undefined, { text: this.text1, }, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { __backing_childText: this.__backing_text1, childText2: this.text1, childText3: this.text1, childText4: this.text1, - } as __Options_Child), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___161337494 = initializers; (((gensym___161337494) == (null)) ? undefined : gensym___161337494.__backing_childText)})) { - this.__backing_childText = new LinkDecoratedVariable("childText", initializers!.__backing_childText!); + this.__backing_childText = STATE_MGMT_FACTORY.makeLink(this, "childText", initializers!.__backing_childText!); }; - this.__backing_childText2 = new StateDecoratedVariable("childText2", ((({let gensym___95513066 = initializers; + this.__backing_childText2 = STATE_MGMT_FACTORY.makeState(this, "childText2", ((({let gensym___95513066 = initializers; (((gensym___95513066) == (null)) ? undefined : gensym___95513066.childText2)})) ?? ("sss"))); - this.__backing_childText3 = new PropDecoratedVariable("childText3", (initializers!.childText3 as string)); - this.__backing_childText4 = new PropDecoratedVariable("childText4", ((({let gensym___162028107 = initializers; + this.__backing_childText3 = STATE_MGMT_FACTORY.makeProp(this, "childText3", (initializers!.childText3 as string)); + this.__backing_childText4 = STATE_MGMT_FACTORY.makeProp(this, "childText4", ((({let gensym___162028107 = initializers; (((gensym___162028107) == (null)) ? undefined : gensym___162028107.childText4)})) ?? ("cc"))); } - public __updateStruct(initializers: __Options_Child | undefined): void { + + public __updateStruct(initializers: (__Options_Child | undefined)): void { if (((({let gensym___77632518 = initializers; (((gensym___77632518) == (null)) ? undefined : gensym___77632518.childText3)})) !== (undefined))) { this.__backing_childText3!.update((initializers!.childText3 as string)); @@ -107,66 +122,95 @@ function main() {} this.__backing_childText4!.update((initializers!.childText4 as string)); } } - private __backing_childText?: LinkDecoratedVariable; + + private __backing_childText?: ILinkDecoratedVariable; + public get childText(): string { return this.__backing_childText!.get(); } + public set childText(value: string) { this.__backing_childText!.set(value); } - private __backing_childText2?: StateDecoratedVariable; + + private __backing_childText2?: IStateDecoratedVariable; + public get childText2(): string { return this.__backing_childText2!.get(); } + public set childText2(value: string) { this.__backing_childText2!.set(value); } - private __backing_childText3?: PropDecoratedVariable; + + private __backing_childText3?: IPropDecoratedVariable; + public get childText3(): string { return this.__backing_childText3!.get(); } + public set childText3(value: string) { this.__backing_childText3!.set(value); } - private __backing_childText4?: PropDecoratedVariable; + + private __backing_childText4?: IPropDecoratedVariable; + public get childText4(): string { return this.__backing_childText4!.get(); } + public set childText4(value: string) { this.__backing_childText4!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { TextInput(undefined, { text: this.childText, }, undefined); } + public constructor() {} + } -interface __Options_Parant { - set text1(text1: string | undefined) - get text1(): string | undefined - set __backing_text1(__backing_text1: DecoratedV1VariableBase | undefined) - get __backing_text1(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_Parant { + @__Link_intrinsic() set text1(text1: (string | undefined)) + + @__Link_intrinsic() get text1(): (string | undefined) + set __backing_text1(__backing_text1: (LinkSourceType | undefined)) + + get __backing_text1(): (LinkSourceType | undefined) + } -interface __Options_Child { - set childText(childText: string | undefined) - get childText(): string | undefined - set __backing_childText(__backing_childText: DecoratedV1VariableBase | undefined) - get __backing_childText(): DecoratedV1VariableBase | undefined - set childText2(childText2: string | undefined) - get childText2(): string | undefined - set __backing_childText2(__backing_childText2: StateDecoratedVariable | undefined) - get __backing_childText2(): StateDecoratedVariable | undefined - set childText3(childText3: string | undefined) - get childText3(): string | undefined - set __backing_childText3(__backing_childText3: PropDecoratedVariable | undefined) - get __backing_childText3(): PropDecoratedVariable | undefined - set childText4(childText4: string | undefined) - get childText4(): string | undefined - set __backing_childText4(__backing_childText4: PropDecoratedVariable | undefined) - get __backing_childText4(): PropDecoratedVariable | undefined +@Component() export interface __Options_Child { + @__Link_intrinsic() set childText(childText: (string | undefined)) + + @__Link_intrinsic() get childText(): (string | undefined) + set __backing_childText(__backing_childText: (LinkSourceType | undefined)) + + get __backing_childText(): (LinkSourceType | undefined) + set childText2(childText2: (string | undefined)) + + get childText2(): (string | undefined) + set __backing_childText2(__backing_childText2: (IStateDecoratedVariable | undefined)) + + get __backing_childText2(): (IStateDecoratedVariable | undefined) + set childText3(childText3: (string | undefined)) + + get childText3(): (string | undefined) + set __backing_childText3(__backing_childText3: (IPropDecoratedVariable | undefined)) + + get __backing_childText3(): (IPropDecoratedVariable | undefined) + set childText4(childText4: (string | undefined)) + + get childText4(): (string | undefined) + set __backing_childText4(__backing_childText4: (IPropDecoratedVariable | undefined)) + + get __backing_childText4(): (IPropDecoratedVariable | undefined) + } `; @@ -176,9 +220,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Link decorated variables passing to other variables', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts index cd9424e90a9b9d10cfb84261831bc31b0f48db4b..c22a650babb3dbd6bdb5d6b62ebbd38b8d11432a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,46 +38,70 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UIDatePickerAttribute as UIDatePickerAttribute } from "@ohos.arkui.component"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + import { EntryPoint as EntryPoint } from "arkui.UserView"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Entry as Entry, Column as Column, Button as Button, DatePicker as DatePicker, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + import { Link as Link, State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class DateComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_DateComponent | undefined, @memo() content: (()=> void) | undefined): void { +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/link/state-to-link", + pageFullPath: "test/demo/mock/decorators/link/state-to-link", + integratedHsp: "false", + } as NavInterface)); + +@Component() final struct DateComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_DateComponent | undefined), @memo() content: ((()=> void) | undefined)): void { if (({let gensym___164314175 = initializers; (((gensym___164314175) == (null)) ? undefined : gensym___164314175.__backing_selectedDate)})) { - this.__backing_selectedDate = new LinkDecoratedVariable("selectedDate", initializers!.__backing_selectedDate!); + this.__backing_selectedDate = STATE_MGMT_FACTORY.makeLink(this, "selectedDate", initializers!.__backing_selectedDate!); }; } - public __updateStruct(initializers: __Options_DateComponent | undefined): void {} - private __backing_selectedDate?: LinkDecoratedVariable; + + public __updateStruct(initializers: (__Options_DateComponent | undefined)): void {} + + private __backing_selectedDate?: ILinkDecoratedVariable; + public get selectedDate(): Date { return this.__backing_selectedDate!.get(); } + public set selectedDate(value: Date) { this.__backing_selectedDate!.set(value); } - @memo() public _build(@memo() style: ((instance: DateComponent)=> DateComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_DateComponent | undefined): void { - Column(undefined, undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.selectedDate.setFullYear(((this.selectedDate.getFullYear()) + (1))); })); return; }), "child increase the year by 1", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.selectedDate = new Date("2023-09-09"); })); @@ -89,32 +114,38 @@ function main() {} }, undefined); })); } + public constructor() {} } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class ParentComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ParentComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_parentSelectedDate = new StateDecoratedVariable("parentSelectedDate", ((({let gensym___80922148 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct ParentComponent extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_parentSelectedDate = STATE_MGMT_FACTORY.makeState(this, "parentSelectedDate", ((({let gensym___80922148 = initializers; (((gensym___80922148) == (null)) ? undefined : gensym___80922148.parentSelectedDate)})) ?? (new Date("2021-08-08")))); } - public __updateStruct(initializers: __Options_ParentComponent | undefined): void {} - private __backing_parentSelectedDate?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_parentSelectedDate?: IStateDecoratedVariable; + public get parentSelectedDate(): Date { return this.__backing_parentSelectedDate!.get(); } + public set parentSelectedDate(value: Date) { this.__backing_parentSelectedDate!.set(value); } - @memo() public _build(@memo() style: ((instance: ParentComponent)=> ParentComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ParentComponent | undefined): void { - Column(undefined, undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate.setMonth(((this.parentSelectedDate.getMonth()) + (1))); })); return; }), "parent increase the month by 1", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate = new Date("2023-07-07"); })); @@ -127,26 +158,36 @@ function main() {} }, undefined); DateComponent._instantiateImpl(undefined, (() => { return new DateComponent(); - }), ({ + }), { __backing_selectedDate: this.__backing_parentSelectedDate, - } as __Options_DateComponent), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -interface __Options_DateComponent { - set selectedDate(selectedDate: Date | undefined) - get selectedDate(): Date | undefined - set __backing_selectedDate(__backing_selectedDate: DecoratedV1VariableBase | undefined) - get __backing_selectedDate(): DecoratedV1VariableBase | undefined +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Component() export interface __Options_DateComponent { + @__Link_intrinsic() set selectedDate(selectedDate: (Date | undefined)) + + @__Link_intrinsic() get selectedDate(): (Date | undefined) + set __backing_selectedDate(__backing_selectedDate: (LinkSourceType | undefined)) + + get __backing_selectedDate(): (LinkSourceType | undefined) + } -interface __Options_ParentComponent { - set parentSelectedDate(parentSelectedDate: Date | undefined) - get parentSelectedDate(): Date | undefined - set __backing_parentSelectedDate(__backing_parentSelectedDate: StateDecoratedVariable | undefined) - get __backing_parentSelectedDate(): StateDecoratedVariable | undefined +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_ParentComponent { + set parentSelectedDate(parentSelectedDate: (Date | undefined)) + + get parentSelectedDate(): (Date | undefined) + set __backing_parentSelectedDate(__backing_parentSelectedDate: (IStateDecoratedVariable | undefined)) + + get __backing_parentSelectedDate(): (IStateDecoratedVariable | undefined) + } class __EntryWrapper extends EntryPoint { @@ -155,7 +196,9 @@ class __EntryWrapper extends EntryPoint { return new ParentComponent(); }), undefined, undefined, undefined); } + public constructor() {} + } `; @@ -165,9 +208,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Link decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..fcfd6824a7a9f31fc325dc5cef14d5ac28dc85a8 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-basic-type.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'local-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localVar1 = STATE_MGMT_FACTORY.makeLocal(this, "localVar1", "stateVar1"); + this.__backing_localVar2 = STATE_MGMT_FACTORY.makeLocal(this, "localVar2", 50); + this.__backing_localVar3 = STATE_MGMT_FACTORY.makeLocal(this, "localVar3", true); + this.__backing_localVar4 = STATE_MGMT_FACTORY.makeLocal(this, "localVar4", undefined); + this.__backing_localVar5 = STATE_MGMT_FACTORY.makeLocal(this, "localVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_localVar1?: ILocalDecoratedVariable; + + public get localVar1(): string { + return this.__backing_localVar1!.get(); + } + + public set localVar1(value: string) { + this.__backing_localVar1!.set(value); + } + + private __backing_localVar2?: ILocalDecoratedVariable; + + public get localVar2(): number { + return this.__backing_localVar2!.get(); + } + + public set localVar2(value: number) { + this.__backing_localVar2!.set(value); + } + + private __backing_localVar3?: ILocalDecoratedVariable; + + public get localVar3(): boolean { + return this.__backing_localVar3!.get(); + } + + public set localVar3(value: boolean) { + this.__backing_localVar3!.set(value); + } + + private __backing_localVar4?: ILocalDecoratedVariable; + + public get localVar4(): undefined { + return this.__backing_localVar4!.get(); + } + + public set localVar4(value: undefined) { + this.__backing_localVar4!.set(value); + } + + private __backing_localVar5?: ILocalDecoratedVariable; + + public get localVar5(): null { + return this.__backing_localVar5!.get(); + } + + public set localVar5(value: null) { + this.__backing_localVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + set localVar3(localVar3: (boolean | undefined)) + + get localVar3(): (boolean | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + set localVar4(localVar4: (undefined | undefined)) + + get localVar4(): (undefined | undefined) + set __backing_localVar4(__backing_localVar4: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar4(): (ILocalDecoratedVariable | undefined) + set localVar5(localVar5: (null | undefined)) + + get localVar5(): (null | undefined) + set __backing_localVar5(__backing_localVar5: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar5(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Local decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad39578d1170981602e56865f07133133151628e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/local-complex-type.test.ts @@ -0,0 +1,349 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'local-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localVar1 = STATE_MGMT_FACTORY.makeLocal(this, "localVar1", new Per(6)); + this.__backing_localVar2 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar2", new Array(3, 6, 8)); + this.__backing_localVar3 = STATE_MGMT_FACTORY.makeLocal(this, "localVar3", StateType.TYPE3); + this.__backing_localVar4 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar4", new Set(new Array("aa", "bb"))); + this.__backing_localVar5 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar5", [true, false]); + this.__backing_localVar6 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar6", new Array(new Per(7), new Per(11))); + this.__backing_localVar7 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar7", [new Per(7), new Per(11)]); + this.__backing_localVar9 = STATE_MGMT_FACTORY.makeLocal(this, "localVar9", new Date("2025-4-23")); + this.__backing_localVar10 = STATE_MGMT_FACTORY.makeLocal>(this, "localVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_localVar11 = STATE_MGMT_FACTORY.makeLocal<(string | number)>(this, "localVar11", 0.0); + this.__backing_localVar12 = STATE_MGMT_FACTORY.makeLocal<(Set | Per)>(this, "localVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_localVar1?: ILocalDecoratedVariable; + + public get localVar1(): Per { + return this.__backing_localVar1!.get(); + } + + public set localVar1(value: Per) { + this.__backing_localVar1!.set(value); + } + + private __backing_localVar2?: ILocalDecoratedVariable>; + + public get localVar2(): Array { + return this.__backing_localVar2!.get(); + } + + public set localVar2(value: Array) { + this.__backing_localVar2!.set(value); + } + + private __backing_localVar3?: ILocalDecoratedVariable; + + public get localVar3(): StateType { + return this.__backing_localVar3!.get(); + } + + public set localVar3(value: StateType) { + this.__backing_localVar3!.set(value); + } + + private __backing_localVar4?: ILocalDecoratedVariable>; + + public get localVar4(): Set { + return this.__backing_localVar4!.get(); + } + + public set localVar4(value: Set) { + this.__backing_localVar4!.set(value); + } + + private __backing_localVar5?: ILocalDecoratedVariable>; + + public get localVar5(): Array { + return this.__backing_localVar5!.get(); + } + + public set localVar5(value: Array) { + this.__backing_localVar5!.set(value); + } + + private __backing_localVar6?: ILocalDecoratedVariable>; + + public get localVar6(): Array { + return this.__backing_localVar6!.get(); + } + + public set localVar6(value: Array) { + this.__backing_localVar6!.set(value); + } + + private __backing_localVar7?: ILocalDecoratedVariable>; + + public get localVar7(): Array { + return this.__backing_localVar7!.get(); + } + + public set localVar7(value: Array) { + this.__backing_localVar7!.set(value); + } + + private __backing_localVar9?: ILocalDecoratedVariable; + + public get localVar9(): Date { + return this.__backing_localVar9!.get(); + } + + public set localVar9(value: Date) { + this.__backing_localVar9!.set(value); + } + + private __backing_localVar10?: ILocalDecoratedVariable>; + + public get localVar10(): Map { + return this.__backing_localVar10!.get(); + } + + public set localVar10(value: Map) { + this.__backing_localVar10!.set(value); + } + + private __backing_localVar11?: ILocalDecoratedVariable<(string | number)>; + + public get localVar11(): (string | number) { + return this.__backing_localVar11!.get(); + } + + public set localVar11(value: (string | number)) { + this.__backing_localVar11!.set(value); + } + + private __backing_localVar12?: ILocalDecoratedVariable<(Set | Per)>; + + public get localVar12(): (Set | Per) { + return this.__backing_localVar12!.get(); + } + + public set localVar12(value: (Set | Per)) { + this.__backing_localVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (Per | undefined)) + + get localVar1(): (Per | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (Array | undefined)) + + get localVar2(): (Array | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable> | undefined) + set localVar3(localVar3: (StateType | undefined)) + + get localVar3(): (StateType | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + set localVar4(localVar4: (Set | undefined)) + + get localVar4(): (Set | undefined) + set __backing_localVar4(__backing_localVar4: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar4(): (ILocalDecoratedVariable> | undefined) + set localVar5(localVar5: (Array | undefined)) + + get localVar5(): (Array | undefined) + set __backing_localVar5(__backing_localVar5: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar5(): (ILocalDecoratedVariable> | undefined) + set localVar6(localVar6: (Array | undefined)) + + get localVar6(): (Array | undefined) + set __backing_localVar6(__backing_localVar6: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar6(): (ILocalDecoratedVariable> | undefined) + set localVar7(localVar7: (Array | undefined)) + + get localVar7(): (Array | undefined) + set __backing_localVar7(__backing_localVar7: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar7(): (ILocalDecoratedVariable> | undefined) + set localVar9(localVar9: (Date | undefined)) + + get localVar9(): (Date | undefined) + set __backing_localVar9(__backing_localVar9: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar9(): (ILocalDecoratedVariable | undefined) + set localVar10(localVar10: (Map | undefined)) + + get localVar10(): (Map | undefined) + set __backing_localVar10(__backing_localVar10: (ILocalDecoratedVariable> | undefined)) + + get __backing_localVar10(): (ILocalDecoratedVariable> | undefined) + set localVar11(localVar11: ((string | number) | undefined)) + + get localVar11(): ((string | number) | undefined) + set __backing_localVar11(__backing_localVar11: (ILocalDecoratedVariable<(string | number)> | undefined)) + + get __backing_localVar11(): (ILocalDecoratedVariable<(string | number)> | undefined) + set localVar12(localVar12: ((Set | Per) | undefined)) + + get localVar12(): ((Set | Per) | undefined) + set __backing_localVar12(__backing_localVar12: (ILocalDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_localVar12(): (ILocalDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Local decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..b85513d93e3025ae35a0aea25a08269683a755d2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/local/static-local.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/local'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'static-local.ets'), +]; + +const pluginTester = new PluginTester('test static @Local decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + +class ABB { + public constructor() {} + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + public static __backing_localVar1: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar1", "stateVar1"); + + public static get localVar1(): string { + return Parent.__backing_localVar1.get(); + } + + public static set localVar1(value: string) { + Parent.__backing_localVar1.set(value); + } + + public static __backing_localVar2: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar2", 50); + + public static get localVar2(): number { + return Parent.__backing_localVar2.get(); + } + + public static set localVar2(value: number) { + Parent.__backing_localVar2.set(value); + } + + public static __backing_localVar3: ILocalDecoratedVariable = STATE_MGMT_FACTORY.makeStaticLocal("localVar3", new ABB()); + + public static get localVar3(): ABB { + return Parent.__backing_localVar3.get(); + } + + public static set localVar3(value: ABB) { + Parent.__backing_localVar3.set(value); + } + + @memo() public build() {} + + public constructor() {} + + static { + + } +} + +@ComponentV2() export interface __Options_Parent { + set localVar1(localVar1: (string | undefined)) + + get localVar1(): (string | undefined) + set __backing_localVar1(__backing_localVar1: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar1(): (ILocalDecoratedVariable | undefined) + set localVar2(localVar2: (number | undefined)) + + get localVar2(): (number | undefined) + set __backing_localVar2(__backing_localVar2: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar2(): (ILocalDecoratedVariable | undefined) + set localVar3(localVar3: (ABB | undefined)) + + get localVar3(): (ABB | undefined) + set __backing_localVar3(__backing_localVar3: (ILocalDecoratedVariable | undefined)) + + get __backing_localVar3(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test static @Local decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5e77e56caef6e0db9af39ba7c7f46bf3f65e6ac --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts @@ -0,0 +1,306 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const LOCAL_STORAGELINK_DIR_PATH: string = 'decorators/localstoragelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LOCAL_STORAGELINK_DIR_PATH, 'localstoragelink-complex-type.ets'), +]; + +const localStorageLinkTransform: Plugins = { + name: 'localStorageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test LocalStorageLink complex type transform', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStorageLinkDecoratedVariable as ILocalStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorageLink as LocalStorageLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/localstoragelink/localstoragelink-complex-type", + pageFullPath: "test/demo/mock/decorators/localstoragelink/localstoragelink-complex-type", + integratedHsp: "false", +} as NavInterface)); + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop1", "arrayA", [1, 2, 3]) + this.__backing_objectA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "objectA", {}) + this.__backing_dateA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "dateA", new Date("2021-08-08")) + this.__backing_setA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop4", "setA", new Set()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop5", "mapA", new Map()) + this.__backing_classA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop7", "classA", new Person("John")) + this.__backing_enumA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop8", "enumA", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayA?: ILocalStorageLinkDecoratedVariable>; + + public get arrayA(): Array { + return this.__backing_arrayA!.get(); + } + + public set arrayA(value: Array) { + this.__backing_arrayA!.set(value); + } + + private __backing_objectA?: ILocalStorageLinkDecoratedVariable; + + public get objectA(): Object { + return this.__backing_objectA!.get(); + } + + public set objectA(value: Object) { + this.__backing_objectA!.set(value); + } + + private __backing_dateA?: ILocalStorageLinkDecoratedVariable; + + public get dateA(): Date { + return this.__backing_dateA!.get(); + } + + public set dateA(value: Date) { + this.__backing_dateA!.set(value); + } + + private __backing_setA?: ILocalStorageLinkDecoratedVariable>; + + public get setA(): Set { + return this.__backing_setA!.get(); + } + + public set setA(value: Set) { + this.__backing_setA!.set(value); + } + + private __backing_mapA?: ILocalStorageLinkDecoratedVariable>; + + public get mapA(): Map { + return this.__backing_mapA!.get(); + } + + public set mapA(value: Map) { + this.__backing_mapA!.set(value); + } + + private __backing_classA?: ILocalStorageLinkDecoratedVariable; + + public get classA(): Person { + return this.__backing_classA!.get(); + } + + public set classA(value: Person) { + this.__backing_classA!.set(value); + } + + private __backing_enumA?: ILocalStorageLinkDecoratedVariable; + + public get enumA(): Status { + return this.__backing_enumA!.get(); + } + + public set enumA(value: Status) { + this.__backing_enumA!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayA(arrayA: (Array | undefined)) + + get arrayA(): (Array | undefined) + set __backing_arrayA(__backing_arrayA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_arrayA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set objectA(objectA: (Object | undefined)) + + get objectA(): (Object | undefined) + set __backing_objectA(__backing_objectA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_objectA(): (ILocalStorageLinkDecoratedVariable | undefined) + set dateA(dateA: (Date | undefined)) + + get dateA(): (Date | undefined) + set __backing_dateA(__backing_dateA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_dateA(): (ILocalStorageLinkDecoratedVariable | undefined) + set setA(setA: (Set | undefined)) + + get setA(): (Set | undefined) + set __backing_setA(__backing_setA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_setA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set mapA(mapA: (Map | undefined)) + + get mapA(): (Map | undefined) + set __backing_mapA(__backing_mapA: (ILocalStorageLinkDecoratedVariable> | undefined)) + + get __backing_mapA(): (ILocalStorageLinkDecoratedVariable> | undefined) + set classA(classA: (Person | undefined)) + + get classA(): (Person | undefined) + set __backing_classA(__backing_classA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_classA(): (ILocalStorageLinkDecoratedVariable | undefined) + set enumA(enumA: (Status | undefined)) + + get enumA(): (Status | undefined) + set __backing_enumA(__backing_enumA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_enumA(): (ILocalStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testLocalStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test LocalStorageLink complex type transform', + [localStorageLinkTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testLocalStorageLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..fea624d8dfba82ca82a499660ec0d7b428c21245 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const LOCAL_STORAGELINK_DIR_PATH: string = 'decorators/localstoragelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LOCAL_STORAGELINK_DIR_PATH, 'localstoragelink-primitive-type.ets'), +]; + +const localStorageLinkTransform: Plugins = { + name: 'localStorageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test LocalStorageLink primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStorageLinkDecoratedVariable as ILocalStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorageLink as LocalStorageLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/localstoragelink/localstoragelink-primitive-type", + pageFullPath: "test/demo/mock/decorators/localstoragelink/localstoragelink-primitive-type", + integratedHsp: "false", +} as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop1", "numA", 33) + this.__backing_stringA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "stringA", "AA") + this.__backing_booleanA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "booleanA", true) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numA?: ILocalStorageLinkDecoratedVariable; + + public get numA(): number { + return this.__backing_numA!.get(); + } + + public set numA(value: number) { + this.__backing_numA!.set(value); + } + + private __backing_stringA?: ILocalStorageLinkDecoratedVariable; + + public get stringA(): string { + return this.__backing_stringA!.get(); + } + + public set stringA(value: string) { + this.__backing_stringA!.set(value); + } + + private __backing_booleanA?: ILocalStorageLinkDecoratedVariable; + + public get booleanA(): boolean { + return this.__backing_booleanA!.get(); + } + + public set booleanA(value: boolean) { + this.__backing_booleanA!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numA(numA: (number | undefined)) + + get numA(): (number | undefined) + set __backing_numA(__backing_numA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_numA(): (ILocalStorageLinkDecoratedVariable | undefined) + set stringA(stringA: (string | undefined)) + + get stringA(): (string | undefined) + set __backing_stringA(__backing_stringA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_stringA(): (ILocalStorageLinkDecoratedVariable | undefined) + set booleanA(booleanA: (boolean | undefined)) + + get booleanA(): (boolean | undefined) + set __backing_booleanA(__backing_booleanA: (ILocalStorageLinkDecoratedVariable | undefined)) + + get __backing_booleanA(): (ILocalStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testLocalStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test LocalStorageLink primitive type transform', + [localStorageLinkTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testLocalStorageLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d6f9b383dd0a23eaf71b0361b1571be25b9218e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-complex-type.test.ts @@ -0,0 +1,279 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/localstorageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'localstorageprop-ref-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @LocalStoragePropRef complex type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStoragePropRefDecoratedVariable as ILocalStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { LocalStoragePropRef as LocalStoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeLocalStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop8", "enumB", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayB?: ILocalStoragePropRefDecoratedVariable>; + + public get arrayB(): Array { + return this.__backing_arrayB!.get(); + } + + public set arrayB(value: Array) { + this.__backing_arrayB!.set(value); + } + + private __backing_objectB?: ILocalStoragePropRefDecoratedVariable; + + public get objectB(): Object { + return this.__backing_objectB!.get(); + } + + public set objectB(value: Object) { + this.__backing_objectB!.set(value); + } + + private __backing_dateB?: ILocalStoragePropRefDecoratedVariable; + + public get dateB(): Date { + return this.__backing_dateB!.get(); + } + + public set dateB(value: Date) { + this.__backing_dateB!.set(value); + } + + private __backing_setB?: ILocalStoragePropRefDecoratedVariable>; + + public get setB(): Set { + return this.__backing_setB!.get(); + } + + public set setB(value: Set) { + this.__backing_setB!.set(value); + } + + private __backing_mapB?: ILocalStoragePropRefDecoratedVariable>; + + public get mapB(): Map { + return this.__backing_mapB!.get(); + } + + public set mapB(value: Map) { + this.__backing_mapB!.set(value); + } + + private __backing_classB?: ILocalStoragePropRefDecoratedVariable; + + public get classB(): Person { + return this.__backing_classB!.get(); + } + + public set classB(value: Person) { + this.__backing_classB!.set(value); + } + + private __backing_enumB?: ILocalStoragePropRefDecoratedVariable; + + public get enumB(): Status { + return this.__backing_enumB!.get(); + } + + public set enumB(value: Status) { + this.__backing_enumB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) + + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_arrayB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) + + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_objectB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) + + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_dateB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) + + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_setB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) + + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (ILocalStoragePropRefDecoratedVariable> | undefined)) + + get __backing_mapB(): (ILocalStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) + + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_classB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) + + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_enumB(): (ILocalStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @LocalStoragePropRef complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..407d8ea1233ee13d9dba68a8260940ac4c80a167 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstorageprop-ref/localstorageprop-ref-primitive-type.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/localstorageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'localstorageprop-ref-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @LocalStoragePropRef primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalStoragePropRefDecoratedVariable as ILocalStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { LocalStoragePropRef as LocalStoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop3", "booleanB", false) + this.__backing_undefinedB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop4", "undefinedB", undefined) + this.__backing_nullB = STATE_MGMT_FACTORY.makeLocalStoragePropRef(this, "Prop5", "nullB", null) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numB?: ILocalStoragePropRefDecoratedVariable; + + public get numB(): number { + return this.__backing_numB!.get(); + } + + public set numB(value: number) { + this.__backing_numB!.set(value); + } + + private __backing_stringB?: ILocalStoragePropRefDecoratedVariable; + + public get stringB(): string { + return this.__backing_stringB!.get(); + } + + public set stringB(value: string) { + this.__backing_stringB!.set(value); + } + + private __backing_booleanB?: ILocalStoragePropRefDecoratedVariable; + + public get booleanB(): boolean { + return this.__backing_booleanB!.get(); + } + + public set booleanB(value: boolean) { + this.__backing_booleanB!.set(value); + } + + private __backing_undefinedB?: ILocalStoragePropRefDecoratedVariable; + + public get undefinedB(): undefined { + return this.__backing_undefinedB!.get(); + } + + public set undefinedB(value: undefined) { + this.__backing_undefinedB!.set(value); + } + + private __backing_nullB?: ILocalStoragePropRefDecoratedVariable; + + public get nullB(): null { + return this.__backing_nullB!.get(); + } + + public set nullB(value: null) { + this.__backing_nullB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) + + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_numB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) + + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_stringB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) + + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_booleanB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set undefinedB(undefinedB: (undefined | undefined)) + + get undefinedB(): (undefined | undefined) + set __backing_undefinedB(__backing_undefinedB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_undefinedB(): (ILocalStoragePropRefDecoratedVariable | undefined) + set nullB(nullB: (null | undefined)) + + get nullB(): (null | undefined) + set __backing_nullB(__backing_nullB: (ILocalStoragePropRefDecoratedVariable | undefined)) + + get __backing_nullB(): (ILocalStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @LocalStoragePropRef primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7289b2b4b52a0c6fcb2c6eeb23234c5d519d91f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/enum-monitor-params.test.ts @@ -0,0 +1,294 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'enum-monitor-params.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator enum parameters transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + +class FFF { + public ff: GGG = new GGG(); + + public constructor() {} + +} + +class GGG { + public constructor() {} + +} + +final class MonitorNames extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: String) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly name1: MonitorNames = new MonitorNames(0, "strArr.0"); + + public static readonly name2: MonitorNames = new MonitorNames(1, "name"); + + public static readonly name3: MonitorNames = new MonitorNames(2, "strArr.0"); + + public static readonly name4: MonitorNames = new MonitorNames(3, "varF.ff"); + + private static readonly #NamesArray: String[] = ["name1", "name2", "name3", "name4"]; + + private static readonly #StringValuesArray: String[] = ["strArr.0", "name", "strArr.0", "varF.ff"]; + + private static readonly #ItemsArray: MonitorNames[] = [MonitorNames.name1, MonitorNames.name2, MonitorNames.name3, MonitorNames.name4]; + + public getName(): String { + return MonitorNames.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): MonitorNames { + for (let i = 0;((i) < (MonitorNames.#NamesArray.length));(++i)) { + if (((name) == (MonitorNames.#NamesArray[i]))) { + return MonitorNames.#ItemsArray[i]; + } + } + throw new Error((("No enum constant MonitorNames.") + (name))); + } + + public static fromValue(value: String): MonitorNames { + for (let i = 0;((i) < (MonitorNames.#StringValuesArray.length));(++i)) { + if (((value) == (MonitorNames.#StringValuesArray[i]))) { + return MonitorNames.#ItemsArray[i]; + } + } + throw new Error((("No enum MonitorNames with value ") + (value))); + } + + public valueOf(): String { + return MonitorNames.#StringValuesArray[this.#ordinal]; + } + + public toString(): String { + return MonitorNames.#StringValuesArray[this.#ordinal]; + } + + public static values(): MonitorNames[] { + return MonitorNames.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: MonitorNames): String { + return e.getName(); + } + +} + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"strArr"}) private __backing_strArr: Array = ["North", "east"]; + + @JSONStringifyIgnore() private __meta_strArr: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private __monitor_changeCCC: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:[MonitorNames.name1, MonitorNames.name2, MonitorNames.name3]}) public changeCCC(monitor: IMonitor) {} + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get strArr(): Array { + this.conditionalAddRef(this.__meta_strArr); + return UIUtils.makeObserved(this.__backing_strArr); + } + + public set strArr(newValue: Array) { + if (((this.__backing_strArr) !== (newValue))) { + this.__backing_strArr = newValue; + this.__meta_strArr.fireChange(); + this.executeOnSubscribingWatches("strArr"); + } + } + + public constructor() { + this.__monitor_changeCCC = STATE_MGMT_FACTORY.makeMonitor([{ + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___127309370 = this.strArr; + (((gensym___127309370) == (null)) ? undefined : gensym___127309370.$_get(0))}); + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }, { + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___214506040 = this.strArr; + (((gensym___214506040) == (null)) ? undefined : gensym___214506040.$_get(0))}); + }), + }], ((_m: IMonitor) => { + this.changeCCC(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_varF = STATE_MGMT_FACTORY.makeLocal(this, "varF", new FFF()); + this.__monitor_changeEEE = STATE_MGMT_FACTORY.makeMonitor([{ + path: "varF.ff", + valueCallback: ((): Any => { + return ({let gensym___55756901 = this.varF; + (((gensym___55756901) == (null)) ? undefined : gensym___55756901.ff)}); + }), + }], ((_m: IMonitor) => { + this.changeEEE(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_varF?: ILocalDecoratedVariable; + + public get varF(): FFF { + return this.__backing_varF!.get(); + } + + public set varF(value: FFF) { + this.__backing_varF!.set(value); + } + + private __monitor_changeEEE: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:[MonitorNames.name4]}) public changeEEE(monitor: IMonitor) {} + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set varF(varF: (FFF | undefined)) + + get varF(): (FFF | undefined) + set __backing_varF(__backing_varF: (ILocalDecoratedVariable | undefined)) + + get __backing_varF(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator enum parameters transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..30dcef186ca41f79f038eb06cceba3a2436e1021 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-before-state-variable.test.ts @@ -0,0 +1,189 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-before-state-variable.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor method declared before state variables', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Monitor as Monitor, Local as Local, IMonitor as IMonitor } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeLocal(this, "message", "Hello World"); + this.__backing_name = STATE_MGMT_FACTORY.makeLocal(this, "name", "Tom"); + this.__backing_age = STATE_MGMT_FACTORY.makeLocal(this, "age", 24); + this.__monitor_onStrChange2 = STATE_MGMT_FACTORY.makeMonitor([{ + path: "message", + valueCallback: ((): Any => { + return this.message; + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange2(_m); + })); + this.__monitor_onStrChange3 = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange3(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __monitor_onStrChange1: (IMonitorDecoratedVariable | undefined); + + private __monitor_onStrChange2: (IMonitorDecoratedVariable | undefined); + + private __monitor_onStrChange3: (IMonitorDecoratedVariable | undefined); + + private __backing_message?: ILocalDecoratedVariable; + + public get message(): string { + return this.__backing_message!.get(); + } + + public set message(value: string) { + this.__backing_message!.set(value); + } + + private __backing_name?: ILocalDecoratedVariable; + + public get name(): string { + return this.__backing_name!.get(); + } + + public set name(value: string) { + this.__backing_name!.set(value); + } + + private __backing_age?: ILocalDecoratedVariable; + + public get age(): number { + return this.__backing_age!.get(); + } + + public set age(value: number) { + this.__backing_age!.set(value); + } + + @Monitor({value:["message", "name"]}) public onStrChange1(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_74 = monitor.value(path); + (((gensym%%_74) == (null)) ? undefined : gensym%%_74.before)})} to \${({let gensym%%_75 = monitor.value(path); + (((gensym%%_75) == (null)) ? undefined : gensym%%_75.now)})}\`); + })); + } + + @Monitor({value:["message", "name"]}) public onStrChange2(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_76 = monitor.value(path); + (((gensym%%_76) == (null)) ? undefined : gensym%%_76.before)})} to \${({let gensym%%_77 = monitor.value(path); + (((gensym%%_77) == (null)) ? undefined : gensym%%_77.now)})}\`); + })); + } + + @Monitor({value:["name"]}) public onStrChange3(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_78 = monitor.value(path); + (((gensym%%_78) == (null)) ? undefined : gensym%%_78.before)})} to \${({let gensym%%_79 = monitor.value(path); + (((gensym%%_79) == (null)) ? undefined : gensym%%_79.now)})}\`); + })); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set message(message: (string | undefined)) + + get message(): (string | undefined) + set __backing_message(__backing_message: (ILocalDecoratedVariable | undefined)) + + get __backing_message(): (ILocalDecoratedVariable | undefined) + set name(name: (string | undefined)) + + get name(): (string | undefined) + set __backing_name(__backing_name: (ILocalDecoratedVariable | undefined)) + + get __backing_name(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + set __backing_age(__backing_age: (ILocalDecoratedVariable | undefined)) + + get __backing_age(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor method declared before state variables', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..b98dbc0ab3c4c505e233803b185a77bf9061f837 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-observedv2-class.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-in-observedv2-class.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator in @ObservedV2 class transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"region"}) private __backing_region: string = "North"; + + @JSONStringifyIgnore() private __meta_region: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"job"}) private __backing_job: string = "Teacher"; + + @JSONStringifyIgnore() private __meta_job: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 25; + + private __monitor_onNameChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onAgeChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["name"]}) public onNameChange(monitor: IMonitor) { + console.info(\`name change from \${({let gensym%%_43 = monitor.value(); + (((gensym%%_43) == (null)) ? undefined : gensym%%_43.before)})} to \${({let gensym%%_44 = monitor.value(); + (((gensym%%_44) == (null)) ? undefined : gensym%%_44.now)})}\`); + } + + @Monitor({value:["age"]}) public onAgeChange(monitor: IMonitor) { + console.info(\`age change from \${({let gensym%%_45 = monitor.value(); + (((gensym%%_45) == (null)) ? undefined : gensym%%_45.before)})} to \${({let gensym%%_46 = monitor.value(); + (((gensym%%_46) == (null)) ? undefined : gensym%%_46.now)})}\`); + } + + @Monitor({value:["region", "job"]}) public onChange(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} change from \${({let gensym%%_47 = monitor.value(path); + (((gensym%%_47) == (null)) ? undefined : gensym%%_47.before)})} to \${({let gensym%%_48 = monitor.value(path); + (((gensym%%_48) == (null)) ? undefined : gensym%%_48.now)})}\`); + })); + } + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get region(): string { + this.conditionalAddRef(this.__meta_region); + return UIUtils.makeObserved(this.__backing_region); + } + + public set region(newValue: string) { + if (((this.__backing_region) !== (newValue))) { + this.__backing_region = newValue; + this.__meta_region.fireChange(); + this.executeOnSubscribingWatches("region"); + } + } + + public get job(): string { + this.conditionalAddRef(this.__meta_job); + return UIUtils.makeObserved(this.__backing_job); + } + + public set job(newValue: string) { + if (((this.__backing_job) !== (newValue))) { + this.__backing_job = newValue; + this.__meta_job.fireChange(); + this.executeOnSubscribingWatches("job"); + } + } + + public constructor() { + this.__monitor_onNameChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onNameChange(_m); + })); + this.__monitor_onAgeChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "age", + valueCallback: ((): Any => { + return this.age; + }), + }], ((_m: IMonitor) => { + this.onAgeChange(_m); + })); + this.__monitor_onChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "region", + valueCallback: ((): Any => { + return this.region; + }), + }, { + path: "job", + valueCallback: ((): Any => { + return this.job; + }), + }], ((_m: IMonitor) => { + this.onChange(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_info = ((({let gensym___130514200 = initializers; + (((gensym___130514200) == (null)) ? undefined : gensym___130514200.info)})) ?? (new Info())); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_info?: Info; + + public get info(): Info { + return (this.__backing_info as Info); + } + + public set info(value: Info) { + this.__backing_info = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.name = "Jack"; + })); + return; + }), "change name", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.age = 26; + })); + return; + }), "change age", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.region = "South"; + })); + return; + }), "change region", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.info.job = "Driver"; + })); + return; + }), "change job", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set info(info: (Info | undefined)) + + get info(): (Info | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator in @ObservedV2 class transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ecc359ecf977c6dca6e41c0d89d9315b6108d838 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-in-struct.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-in-struct.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator in struct transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, Local as Local, IMonitor as IMonitor } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeLocal(this, "message", "Hello World"); + this.__backing_name = STATE_MGMT_FACTORY.makeLocal(this, "name", "Tom"); + this.__backing_age = STATE_MGMT_FACTORY.makeLocal(this, "age", 24); + this.__monitor_onStrChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "message", + valueCallback: ((): Any => { + return this.message; + }), + }, { + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onStrChange(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_message?: ILocalDecoratedVariable; + + public get message(): string { + return this.__backing_message!.get(); + } + + public set message(value: string) { + this.__backing_message!.set(value); + } + + private __backing_name?: ILocalDecoratedVariable; + + public get name(): string { + return this.__backing_name!.get(); + } + + public set name(value: string) { + this.__backing_name!.set(value); + } + + private __backing_age?: ILocalDecoratedVariable; + + public get age(): number { + return this.__backing_age!.get(); + } + + public set age(value: number) { + this.__backing_age!.set(value); + } + + private __monitor_onStrChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["message", "name"]}) public onStrChange(monitor: IMonitor) { + monitor.dirty.forEach(((path: string) => { + console.info(\`\${path} changed from \${({let gensym%%_43 = monitor.value(path); + (((gensym%%_43) == (null)) ? undefined : gensym%%_43.before)})} to \${({let gensym%%_44 = monitor.value(path); + (((gensym%%_44) == (null)) ? undefined : gensym%%_44.now)})}\`); + })); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.message += "!"; + this.name = "Jack"; + })); + return; + }), "change string", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set message(message: (string | undefined)) + + get message(): (string | undefined) + set __backing_message(__backing_message: (ILocalDecoratedVariable | undefined)) + + get __backing_message(): (ILocalDecoratedVariable | undefined) + set name(name: (string | undefined)) + + get name(): (string | undefined) + set __backing_name(__backing_name: (ILocalDecoratedVariable | undefined)) + + get __backing_name(): (ILocalDecoratedVariable | undefined) + set age(age: (number | undefined)) + + get age(): (number | undefined) + set __backing_age(__backing_age: (ILocalDecoratedVariable | undefined)) + + get __backing_age(): (ILocalDecoratedVariable | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator in struct transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..5479104e5cff28e1eef7d1057279cdefa7d8e63d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/monitor/monitor-params.test.ts @@ -0,0 +1,357 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/monitor'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'monitor-params.ets'), +]; + +const pluginTester = new PluginTester('test @Monitor decorator parameters transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { IMonitorDecoratedVariable as IMonitorDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Button as Button } from "@ohos.arkui.component"; + +import { Monitor as Monitor, IMonitor as IMonitor, ObservedV2 as ObservedV2, Trace as Trace, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class AAA { + public aaa: BBB = new BBB(); + + public constructor() {} + +} + +class BBB { + public bbb: CCC = new CCC(); + + public constructor() {} + +} + +class CCC { + public ccc: Array = new Array(new DDD(10), new DDD(12), new DDD(16)); + + public constructor() {} + +} + +class DDD { + public dd: Array; + + public constructor(dd: number) { + this.dd = []; + for (let i = 4;((i) < (dd));(i++)) { + this.dd.push(i); + } + } + +} + +class EEE { + public ee: FFF = new FFF(); + + public constructor() {} + +} + +class FFF { + public ff: GGG = new GGG(); + + public constructor() {} + +} + +class GGG { + public constructor() {} + +} + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "Tom"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"strArr"}) private __backing_strArr: Array = ["North", "east"]; + + @JSONStringifyIgnore() private __meta_strArr: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"job"}) private __backing_job: AAA = new AAA(); + + @JSONStringifyIgnore() private __meta_job: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public age: number = 25; + + private __monitor_onNameChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onAgeChange: (IMonitorDecoratedVariable | undefined); + + private __monitor_onJobChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["name"]}) public onNameChange(monitor: IMonitor) {} + + @Monitor({value:["strArr.0", "age"]}) public onAgeChange(monitor: IMonitor) {} + + @Monitor({value:["job.aaa.bbb.ccc.1.dd.0"]}) public onJobChange(monitor: IMonitor) {} + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get strArr(): Array { + this.conditionalAddRef(this.__meta_strArr); + return UIUtils.makeObserved(this.__backing_strArr); + } + + public set strArr(newValue: Array) { + if (((this.__backing_strArr) !== (newValue))) { + this.__backing_strArr = newValue; + this.__meta_strArr.fireChange(); + this.executeOnSubscribingWatches("strArr"); + } + } + + public get job(): AAA { + this.conditionalAddRef(this.__meta_job); + return UIUtils.makeObserved(this.__backing_job); + } + + public set job(newValue: AAA) { + if (((this.__backing_job) !== (newValue))) { + this.__backing_job = newValue; + this.__meta_job.fireChange(); + this.executeOnSubscribingWatches("job"); + } + } + + public constructor() { + this.__monitor_onNameChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "name", + valueCallback: ((): Any => { + return this.name; + }), + }], ((_m: IMonitor) => { + this.onNameChange(_m); + })); + this.__monitor_onAgeChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "strArr.0", + valueCallback: ((): Any => { + return ({let gensym___127309370 = this.strArr; + (((gensym___127309370) == (null)) ? undefined : gensym___127309370.$_get(0))}); + }), + }, { + path: "age", + valueCallback: ((): Any => { + return this.age; + }), + }], ((_m: IMonitor) => { + this.onAgeChange(_m); + })); + this.__monitor_onJobChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "job.aaa.bbb.ccc.1.dd.0", + valueCallback: ((): Any => { + return ({let gensym___1661894 = ({let gensym___87426618 = ({let gensym___223024592 = ({let gensym___215921992 = ({let gensym___22088104 = ({let gensym___245550938 = this.job; + (((gensym___245550938) == (null)) ? undefined : gensym___245550938.aaa)}); + (((gensym___22088104) == (null)) ? undefined : gensym___22088104.bbb)}); + (((gensym___215921992) == (null)) ? undefined : gensym___215921992.ccc)}); + (((gensym___223024592) == (null)) ? undefined : gensym___223024592.$_get(1))}); + (((gensym___87426618) == (null)) ? undefined : gensym___87426618.dd)}); + (((gensym___1661894) == (null)) ? undefined : gensym___1661894.$_get(0))}); + }), + }], ((_m: IMonitor) => { + this.onJobChange(_m); + })); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_per = STATE_MGMT_FACTORY.makeLocal(this, "per", new EEE()); + this.__backing_v1 = STATE_MGMT_FACTORY.makeLocal(this, "v1", true); + this.__backing_numArr = STATE_MGMT_FACTORY.makeLocal>(this, "numArr", ["1", "3", "5"]); + this.__monitor_onPerChange = STATE_MGMT_FACTORY.makeMonitor([{ + path: "per.ee.ff", + valueCallback: ((): Any => { + return ({let gensym___164069504 = ({let gensym___239523226 = this.per; + (((gensym___239523226) == (null)) ? undefined : gensym___239523226.ee)}); + (((gensym___164069504) == (null)) ? undefined : gensym___164069504.ff)}); + }), + }, { + path: "v1", + valueCallback: ((): Any => { + return this.v1; + }), + }, { + path: "numArr.1", + valueCallback: ((): Any => { + return ({let gensym___124152275 = this.numArr; + (((gensym___124152275) == (null)) ? undefined : gensym___124152275.$_get(1))}); + }), + }], ((_m: IMonitor) => { + this.onPerChange(_m); + })); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_per?: ILocalDecoratedVariable; + + public get per(): EEE { + return this.__backing_per!.get(); + } + + public set per(value: EEE) { + this.__backing_per!.set(value); + } + + private __backing_v1?: ILocalDecoratedVariable; + + public get v1(): boolean { + return this.__backing_v1!.get(); + } + + public set v1(value: boolean) { + this.__backing_v1!.set(value); + } + + private __backing_numArr?: ILocalDecoratedVariable>; + + public get numArr(): Array { + return this.__backing_numArr!.get(); + } + + public set numArr(value: Array) { + this.__backing_numArr!.set(value); + } + + private __monitor_onPerChange: (IMonitorDecoratedVariable | undefined); + + @Monitor({value:["per.ee.ff", "v1", "numArr.1"]}) public onPerChange(monitor: IMonitor) {} + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set per(per: (EEE | undefined)) + + get per(): (EEE | undefined) + set __backing_per(__backing_per: (ILocalDecoratedVariable | undefined)) + + get __backing_per(): (ILocalDecoratedVariable | undefined) + set v1(v1: (boolean | undefined)) + + get v1(): (boolean | undefined) + set __backing_v1(__backing_v1: (ILocalDecoratedVariable | undefined)) + + get __backing_v1(): (ILocalDecoratedVariable | undefined) + set numArr(numArr: (Array | undefined)) + + get numArr(): (Array | undefined) + set __backing_numArr(__backing_numArr: (ILocalDecoratedVariable> | undefined)) + + get __backing_numArr(): (ILocalDecoratedVariable> | undefined) + +} + +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Monitor decorator parameters transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..60707178b77bdc482e570426c2f660f62b3c6f9a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts @@ -0,0 +1,253 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBJECTLINK_DIR_PATH: string = 'decorators/objectlink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBJECTLINK_DIR_PATH, 'objectlink-basic.ets'), +]; + +const objectlinkTrackTransform: Plugins = { + name: 'objectlink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test objectlink basic transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, ObjectLink as ObjectLink } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + +} + +@Observed() class B implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_objectlinkvar = STATE_MGMT_FACTORY.makeObjectLink(this, "objectlinkvar", (({let gensym___248819442 = initializers; + (((gensym___248819442) == (null)) ? undefined : gensym___248819442.objectlinkvar)}) as A)) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { + if (((({let gensym___97362509 = initializers; + (((gensym___97362509) == (null)) ? undefined : gensym___97362509.objectlinkvar)})) !== (undefined))) { + this.__backing_objectlinkvar!.update(initializers!.objectlinkvar!); + } + } + + private __backing_objectlinkvar?: IObjectLinkDecoratedVariable; + + public get objectlinkvar(): A { + return this.__backing_objectlinkvar!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() final struct MyStateSample2 extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample2 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_objectlinkvar1 = STATE_MGMT_FACTORY.makeObjectLink<(A | undefined)>(this, "objectlinkvar1", (({let gensym___219806589 = initializers; + (((gensym___219806589) == (null)) ? undefined : gensym___219806589.objectlinkvar1)}) as (A | undefined))) + this.__backing_objectlinkvar2 = STATE_MGMT_FACTORY.makeObjectLink<(A | B)>(this, "objectlinkvar2", (({let gensym___217261862 = initializers; + (((gensym___217261862) == (null)) ? undefined : gensym___217261862.objectlinkvar2)}) as (A | B))) + this.__backing_objectlinkvar3 = STATE_MGMT_FACTORY.makeObjectLink<(A | B | null)>(this, "objectlinkvar3", (({let gensym___199257778 = initializers; + (((gensym___199257778) == (null)) ? undefined : gensym___199257778.objectlinkvar3)}) as (A | B | null))) + } + + public __updateStruct(initializers: (__Options_MyStateSample2 | undefined)): void { + if (((({let gensym___82770935 = initializers; + (((gensym___82770935) == (null)) ? undefined : gensym___82770935.objectlinkvar1)})) !== (undefined))) { + this.__backing_objectlinkvar1!.update(initializers!.objectlinkvar1!); + } + if (((({let gensym___225818999 = initializers; + (((gensym___225818999) == (null)) ? undefined : gensym___225818999.objectlinkvar2)})) !== (undefined))) { + this.__backing_objectlinkvar2!.update(initializers!.objectlinkvar2!); + } + if (((({let gensym___3063329 = initializers; + (((gensym___3063329) == (null)) ? undefined : gensym___3063329.objectlinkvar3)})) !== (undefined))) { + this.__backing_objectlinkvar3!.update(initializers!.objectlinkvar3!); + } + } + + private __backing_objectlinkvar1?: IObjectLinkDecoratedVariable<(A | undefined)>; + + public get objectlinkvar1(): (A | undefined) { + return this.__backing_objectlinkvar1!.get(); + } + + private __backing_objectlinkvar2?: IObjectLinkDecoratedVariable<(A | B)>; + + public get objectlinkvar2(): (A | B) { + return this.__backing_objectlinkvar2!.get(); + } + + private __backing_objectlinkvar3?: IObjectLinkDecoratedVariable<(A | B | null)>; + + public get objectlinkvar3(): (A | B | null) { + return this.__backing_objectlinkvar3!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set objectlinkvar(objectlinkvar: (A | undefined)) + + get objectlinkvar(): (A | undefined) + set __backing_objectlinkvar(__backing_objectlinkvar: (IObjectLinkDecoratedVariable | undefined)) + + get __backing_objectlinkvar(): (IObjectLinkDecoratedVariable | undefined) + +} + +@Component() export interface __Options_MyStateSample2 { + set objectlinkvar1(objectlinkvar1: ((A | undefined) | undefined)) + + get objectlinkvar1(): ((A | undefined) | undefined) + set __backing_objectlinkvar1(__backing_objectlinkvar1: (IObjectLinkDecoratedVariable<(A | undefined)> | undefined)) + + get __backing_objectlinkvar1(): (IObjectLinkDecoratedVariable<(A | undefined)> | undefined) + set objectlinkvar2(objectlinkvar2: ((A | B) | undefined)) + + get objectlinkvar2(): ((A | B) | undefined) + set __backing_objectlinkvar2(__backing_objectlinkvar2: (IObjectLinkDecoratedVariable<(A | B)> | undefined)) + + get __backing_objectlinkvar2(): (IObjectLinkDecoratedVariable<(A | B)> | undefined) + set objectlinkvar3(objectlinkvar3: ((A | B | null) | undefined)) + + get objectlinkvar3(): ((A | B | null) | undefined) + set __backing_objectlinkvar3(__backing_objectlinkvar3: (IObjectLinkDecoratedVariable<(A | B | null)> | undefined)) + + get __backing_objectlinkvar3(): (IObjectLinkDecoratedVariable<(A | B | null)> | undefined) + +} +`; + +function testObjectLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test objectlink basic transform', + [objectlinkTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObjectLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..640a8e961b595b0705a6194f48f0e016f89843ee --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts @@ -0,0 +1,310 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBJECTLINK_DIR_PATH: string = 'decorators/objectlink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBJECTLINK_DIR_PATH, 'objectlink-observed.ets'), +]; + +const objectlinkTrackTransform: Plugins = { + name: 'objectlink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test objectlink observed transform', buildConfig); + +const expectedScript: string = ` + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry, Column as Column, Button as Button, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State, ObjectLink as ObjectLink, Observed as Observed } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/objectlink/objectlink-observed", + pageFullPath: "test/demo/mock/decorators/objectlink/objectlink-observed", + integratedHsp: "false", + } as NavInterface)); + +@Observed() class DateClass extends Date implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor(args: (number | string)) { + super(args); + } + +} + +@Observed() class NewDate implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"data"}) private __backing_data: DateClass = new DateClass(11); + + public constructor(data: DateClass) { + this.data = data; + } + + public get data(): DateClass { + this.conditionalAddRef(this.__meta); + return this.__backing_data; + } + + public set data(newValue: DateClass) { + if (((this.__backing_data) !== (newValue))) { + this.__backing_data = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("data"); + } + } + +} + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_label = ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? ("date")); + this.__backing_data = STATE_MGMT_FACTORY.makeObjectLink(this, "data", (({let gensym___209155591 = initializers; + (((gensym___209155591) == (null)) ? undefined : gensym___209155591.data)}) as DateClass)) + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___232946400 = initializers; + (((gensym___232946400) == (null)) ? undefined : gensym___232946400.data)})) !== (undefined))) { + this.__backing_data!.update(initializers!.data!); + } + } + + private __backing_label?: string; + + public get label(): string { + return (this.__backing_label as string); + } + + public set label(value: string) { + this.__backing_label = value; + } + + private __backing_data?: IObjectLinkDecoratedVariable; + + public get data(): DateClass { + return this.__backing_data!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.data.setDate(((this.data.getDate()) + (1))); + })); + return; + }), "child increase the day by 1", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Parent extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_newData = STATE_MGMT_FACTORY.makeState(this, "newData", ((({let gensym___225289068 = initializers; + (((gensym___225289068) == (null)) ? undefined : gensym___225289068.newData)})) ?? (new NewDate(new DateClass("2023-1-1"))))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_newData?: IStateDecoratedVariable; + + public get newData(): NewDate { + return this.__backing_newData!.get(); + } + + public set newData(value: NewDate) { + this.__backing_newData!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + label: "date", + data: this.newData.data, + }, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.newData.data = new DateClass("2023-07-07"); + })); + return; + }), "parent update the new date", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.newData = new NewDate(new DateClass("2023-08-20")); + })); + return; + }), "ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set label(label: (string | undefined)) + + get label(): (string | undefined) + set data(data: (DateClass | undefined)) + + get data(): (DateClass | undefined) + set __backing_data(__backing_data: (IObjectLinkDecoratedVariable | undefined)) + + get __backing_data(): (IObjectLinkDecoratedVariable | undefined) + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Parent { + set newData(newData: (NewDate | undefined)) + + get newData(): (NewDate | undefined) + set __backing_newData(__backing_newData: (IStateDecoratedVariable | undefined)) + + get __backing_newData(): (IStateDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Parent._instantiateImpl(undefined, (() => { + return new Parent(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testObjectLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test objectlink observed transform', + [objectlinkTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObjectLinkTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce55069e0f757221fd12d01197ecb6356e5e6814 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observed-track'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observed-jsonrename.ets'), +]; + +const observedJsonRenameTransform: Plugins = { + name: 'observedJsonRename', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed class transform with JsonRename annotation', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@Observed() class testJsonRename implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name3"}) public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONRename({value:"name5"}) private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name6"}) @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name8"}) @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return this.__backing_var2; + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return this.__backing_var5; + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return this.__backing_var7; + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return this.__backing_var8; + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedJsonRenameTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed only transform', + [observedJsonRenameTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObservedJsonRenameTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c398f8afe6f949253860e81c0812c9a3b762d46 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observed-track'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observed-jsonstringifyignore.ets'), +]; + +const observedJsonStringifyIgnoreTransform: Plugins = { + name: 'observedJsonStringifyIgnore', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed class transform with JsonStringifyIgnore annotation', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@Observed() class testJSONStringifyIgnore implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONStringifyIgnore() private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return this.__backing_var2; + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return this.__backing_var5; + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return this.__backing_var7; + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return this.__backing_var8; + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedJsonStringifyIgnoreTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed only transform', + [observedJsonStringifyIgnoreTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObservedJsonStringifyIgnoreTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts index 08e0ec65b6f512f4a15e88aadad83489868eacf0..c5f0105b6960bf6d898de3eb23e73914de640061 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed only transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,63 +80,68 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propA: number = 1; + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackA: number = 2; + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; + + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; public constructor() {} public get propA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propA; } public set propA(newValue: number) { if (((this.__backing_propA) !== (newValue))) { this.__backing_propA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propA"); } } public get trackA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackA; } public set trackA(newValue: number) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -147,9 +150,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed only transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts index 5bd7f75517d71041264a000359ffd6c6319532c8..61ae1b6c8a3403ac5b2c6d87057d6429a1615fd9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with class property', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -72,8 +70,8 @@ class Info { } -class E implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class E implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -87,36 +85,43 @@ class E implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propE: Info = new Info(); - private __backing_trackE: BackingValue = new BackingValue(new Info()); + @JSONRename({newName:"trackE"}) private __backing_trackE: Info = new Info(); - private __meta_trackE: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackE(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackE.addRef(); - } - setObservationDepth(this.__backing_trackE.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_trackE.value; + this.conditionalAddRef(this.__meta_trackE); + return this.__backing_trackE; } public set trackE(newValue: Info) { - if (((this.__backing_trackE.value) !== (newValue))) { - this.__backing_trackE.value = newValue; - this.__meta_trackE.fireChange(); - this.executeOnSubscribingWatches("trackE"); + if (((this.__backing_trackE) !== (newValue))) { + this.__backing_trackE = newValue; + this.__meta_trackE.fireChange(); + this.executeOnSubscribingWatches("trackE"); } } } -@Observed() class E1 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class E1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -130,62 +135,66 @@ class E implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propE1: BackingValue = new BackingValue(new Info()); + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackE1: BackingValue = new BackingValue(new Info()); + @JSONRename({newName:"propE1"}) private __backing_propE1: Info = new Info(); + + @JSONRename({newName:"trackE1"}) private __backing_trackE1: Info = new Info(); public constructor() {} public get propE1(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_propE1.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_propE1.value; + this.conditionalAddRef(this.__meta); + return this.__backing_propE1; } public set propE1(newValue: Info) { - if (((this.__backing_propE1.value) !== (newValue))) { - this.__backing_propE1.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propE1"); + if (((this.__backing_propE1) !== (newValue))) { + this.__backing_propE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propE1"); } } public get trackE1(): Info { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_trackE1.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_trackE1.value; + this.conditionalAddRef(this.__meta); + return this.__backing_trackE1; } public set trackE1(newValue: Info) { - if (((this.__backing_trackE1.value) !== (newValue))) { - this.__backing_trackE1.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackE1"); + if (((this.__backing_trackE1) !== (newValue))) { + this.__backing_trackE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackE1"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } `; @@ -196,9 +205,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with class property', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts index 371c48a6cdcc181bc867a0a93f514b67615c6bc7..e1ae3d003d969e6385ab4b810c435dde272bec38 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with complex type', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; @@ -67,7 +68,13 @@ import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagemen function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/observed-track/observed-track-complex-type", + pageFullPath: "test/demo/mock/decorators/observed-track/observed-track-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public constructor() {} @@ -142,8 +149,8 @@ final class Status extends BaseEnum { } -@Observed() class mixed1 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class mixed1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -157,51 +164,61 @@ final class Status extends BaseEnum { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __backing_numA: number = 33; + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_numA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_stringA: string = "AA"; + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_stringA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_booleanA: boolean = true; + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_booleanA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_arrayA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_objectA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_dateA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_setA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_mapA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_unionA: string | undefined = ""; + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_unionA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_classA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_enumA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public numB: number = 33; @@ -219,7 +236,7 @@ final class Status extends BaseEnum { public mapB: Map = new Map(); - public unionB: string | undefined = ""; + public unionB: (string | undefined) = ""; public classB: Person = new Person(); @@ -228,181 +245,152 @@ final class Status extends BaseEnum { public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_numA.addRef(); - } + this.conditionalAddRef(this.__meta_numA); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta_numA.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_stringA.addRef(); - } + this.conditionalAddRef(this.__meta_stringA); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta_stringA.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_booleanA.addRef(); - } + this.conditionalAddRef(this.__meta_booleanA); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta_booleanA.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_arrayA.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta_arrayA); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta_arrayA.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_objectA.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta_objectA); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta_objectA.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_dateA.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta_dateA); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta_dateA.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_setA.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta_setA); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta_setA.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_mapA.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta_mapA); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta_mapA.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_unionA.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta_unionA.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_classA.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta_classA); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta_classA.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_enumA.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta_enumA); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta_enumA.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -@Observed() class mixed2 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class mixed2 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -416,210 +404,191 @@ final class Status extends BaseEnum { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __backing_numA: number = 33; + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_stringA: string = "AA"; + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_booleanA: boolean = true; + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_unionA: string | undefined = ""; + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); + + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -class mixed3 implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class mixed3 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -633,240 +602,221 @@ class mixed3 implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __backing_numA: number = 33; + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_numA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; - private __backing_stringA: string = "AA"; + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_stringA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; - private __backing_booleanA: boolean = true; + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_booleanA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; - private __backing_arrayA: BackingValue> = new BackingValue>([1, 2, 3]); + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_arrayA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; - private __backing_objectA: BackingValue = new BackingValue({}); + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_objectA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; - private __backing_dateA: BackingValue = new BackingValue(new Date("2021-08-08")); + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_dateA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); - private __backing_setA: BackingValue> = new BackingValue>(new Set()); + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_setA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); - private __backing_mapA: BackingValue> = new BackingValue>(new Map()); + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_mapA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); - private __backing_unionA: string | undefined = ""; + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_unionA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; - private __backing_classA: BackingValue = new BackingValue(new Person()); + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_classA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); - private __backing_enumA: BackingValue = new BackingValue(Status.NotFound); + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __meta_enumA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get numA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_numA.addRef(); - } + this.conditionalAddRef(this.__meta_numA); return this.__backing_numA; } public set numA(newValue: number) { if (((this.__backing_numA) !== (newValue))) { this.__backing_numA = newValue; - this.__meta_numA.fireChange(); - this.executeOnSubscribingWatches("numA"); + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); } } public get stringA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_stringA.addRef(); - } + this.conditionalAddRef(this.__meta_stringA); return this.__backing_stringA; } public set stringA(newValue: string) { if (((this.__backing_stringA) !== (newValue))) { this.__backing_stringA = newValue; - this.__meta_stringA.fireChange(); - this.executeOnSubscribingWatches("stringA"); + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); } } public get booleanA(): boolean { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_booleanA.addRef(); - } + this.conditionalAddRef(this.__meta_booleanA); return this.__backing_booleanA; } public set booleanA(newValue: boolean) { if (((this.__backing_booleanA) !== (newValue))) { this.__backing_booleanA = newValue; - this.__meta_booleanA.fireChange(); - this.executeOnSubscribingWatches("booleanA"); + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); } } public get arrayA(): Array { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_arrayA.addRef(); - } - setObservationDepth(this.__backing_arrayA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_arrayA.value; + this.conditionalAddRef(this.__meta_arrayA); + return this.__backing_arrayA; } public set arrayA(newValue: Array) { - if (((this.__backing_arrayA.value) !== (newValue))) { - this.__backing_arrayA.value = newValue; - this.__meta_arrayA.fireChange(); - this.executeOnSubscribingWatches("arrayA"); + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); } } public get objectA(): Object { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_objectA.addRef(); - } - setObservationDepth(this.__backing_objectA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_objectA.value; + this.conditionalAddRef(this.__meta_objectA); + return this.__backing_objectA; } public set objectA(newValue: Object) { - if (((this.__backing_objectA.value) !== (newValue))) { - this.__backing_objectA.value = newValue; - this.__meta_objectA.fireChange(); - this.executeOnSubscribingWatches("objectA"); + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); } } public get dateA(): Date { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_dateA.addRef(); - } - setObservationDepth(this.__backing_dateA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_dateA.value; + this.conditionalAddRef(this.__meta_dateA); + return this.__backing_dateA; } public set dateA(newValue: Date) { - if (((this.__backing_dateA.value) !== (newValue))) { - this.__backing_dateA.value = newValue; - this.__meta_dateA.fireChange(); - this.executeOnSubscribingWatches("dateA"); + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); } } public get setA(): Set { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_setA.addRef(); - } - setObservationDepth(this.__backing_setA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_setA.value; + this.conditionalAddRef(this.__meta_setA); + return this.__backing_setA; } public set setA(newValue: Set) { - if (((this.__backing_setA.value) !== (newValue))) { - this.__backing_setA.value = newValue; - this.__meta_setA.fireChange(); - this.executeOnSubscribingWatches("setA"); + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); } } public get mapA(): Map { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_mapA.addRef(); - } - setObservationDepth(this.__backing_mapA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_mapA.value; + this.conditionalAddRef(this.__meta_mapA); + return this.__backing_mapA; } public set mapA(newValue: Map) { - if (((this.__backing_mapA.value) !== (newValue))) { - this.__backing_mapA.value = newValue; - this.__meta_mapA.fireChange(); - this.executeOnSubscribingWatches("mapA"); + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); } } - public get unionA(): string | undefined { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_unionA.addRef(); - } + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); return this.__backing_unionA; } - public set unionA(newValue: string | undefined) { + public set unionA(newValue: (string | undefined)) { if (((this.__backing_unionA) !== (newValue))) { this.__backing_unionA = newValue; - this.__meta_unionA.fireChange(); - this.executeOnSubscribingWatches("unionA"); + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); } } public get classA(): Person { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_classA.addRef(); - } - setObservationDepth(this.__backing_classA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_classA.value; + this.conditionalAddRef(this.__meta_classA); + return this.__backing_classA; } public set classA(newValue: Person) { - if (((this.__backing_classA.value) !== (newValue))) { - this.__backing_classA.value = newValue; - this.__meta_classA.fireChange(); - this.executeOnSubscribingWatches("classA"); + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); } } public get enumA(): Status { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_enumA.addRef(); - } - setObservationDepth(this.__backing_enumA.value, ((this._permissibleAddRefDepth) - (1))); - return this.__backing_enumA.value; + this.conditionalAddRef(this.__meta_enumA); + return this.__backing_enumA; } public set enumA(newValue: Status) { - if (((this.__backing_enumA.value) !== (newValue))) { - this.__backing_enumA.value = newValue; - this.__meta_enumA.fireChange(); - this.executeOnSubscribingWatches("enumA"); + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); } } } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { } @@ -888,9 +838,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with complex type', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts index f3f3a001e2c591788fd4d8553b3d78cdbcf1a73e..90d7b3ebb44e6791f72b8020fc29851de115f96a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with extends', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,43 +80,49 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_propA: number = 1; + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; - private __backing_trackA: number = 2; + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; public constructor() {} public get propA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propA; } public set propA(newValue: number) { if (((this.__backing_propA) !== (newValue))) { this.__backing_propA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propA"); } } public get trackA(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackA; } public set trackA(newValue: number) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } @@ -131,8 +135,8 @@ class G extends A { } -@Observed() class H extends G implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class H extends G implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -146,46 +150,53 @@ class G extends A { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; - private __backing_propG: number = 1; + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __meta_propG: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONRename({newName:"propG"}) private __backing_propG: number = 1; + + @JSONStringifyIgnore() private __meta_propG: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get propG(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_propG.addRef(); - } + this.conditionalAddRef(this.__meta_propG); return this.__backing_propG; } public set propG(newValue: number) { if (((this.__backing_propG) !== (newValue))) { this.__backing_propG = newValue; - this.__meta_propG.fireChange(); - this.executeOnSubscribingWatches("propG"); + this.__meta_propG.fireChange(); + this.executeOnSubscribingWatches("propG"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -194,9 +205,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with extends', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts index f71a5e9220baae369451ae7da09f53299cf542b0..067c357e0637dee7341febcef4c428397b74fbf9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed track transform with implements', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -81,8 +79,8 @@ interface trackInterface { } -@Observed() class F implements PropInterface, trackInterface, IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class F implements PropInterface, trackInterface, IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -96,63 +94,68 @@ interface trackInterface { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } - private __meta: MutableStateMeta = new MutableStateMeta("@Observe properties (no @Track)"); + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } - private __backing_propF: number = 1; + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - private __backing_trackF: number = 2; + @JSONRename({newName:"propF"}) private __backing_propF: number = 1; + + @JSONRename({newName:"trackF"}) private __backing_trackF: number = 2; public constructor() {} - public set propF(newValue: number) { + set propF(newValue: number) { if (((this.__backing_propF) !== (newValue))) { this.__backing_propF = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("propF"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propF"); } } public get propF(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_propF; } - public set trackF(newValue: number) { + set trackF(newValue: number) { if (((this.__backing_trackF) !== (newValue))) { this.__backing_trackF = newValue; - this.__meta.fireChange(); - this.executeOnSubscribingWatches("trackF"); + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackF"); } } public get trackF(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta.addRef(); - } + this.conditionalAddRef(this.__meta); return this.__backing_trackF; } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -161,9 +164,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed track transform with implements', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts index 9f28609dcd64f3b519634444a4aefb6cda4ade5f..15e871f58a2c783ef7fec910a4ff3252d0d30bfa 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test observed with track transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -65,10 +63,8 @@ import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagemen function main() {} - - -@Observed() class B implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class B implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,48 +78,74 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propB: number = 1; - private __backing_trackB: number = 2; + @JSONRename({newName:"trackB"}) private __backing_trackB: number = 2; - private __meta_trackB: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); - public constructor() {} + @JSONRename({newName:"newProp"}) private __backing_newProp?: boolean; + + @JSONStringifyIgnore() private __meta_newProp: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor(newProp: boolean) { + this.newProp = newProp; + } public get trackB(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackB.addRef(); - } + this.conditionalAddRef(this.__meta_trackB); return this.__backing_trackB; } public set trackB(newValue: number) { if (((this.__backing_trackB) !== (newValue))) { this.__backing_trackB = newValue; - this.__meta_trackB.fireChange(); - this.executeOnSubscribingWatches("trackB"); + this.__meta_trackB.fireChange(); + this.executeOnSubscribingWatches("trackB"); + } + } + + public get newProp(): boolean { + this.conditionalAddRef(this.__meta_newProp); + return (this.__backing_newProp as boolean); + } + + public set newProp(newValue: boolean) { + if (((this.__backing_newProp) !== (newValue))) { + this.__backing_newProp = newValue; + this.__meta_newProp.fireChange(); + this.executeOnSubscribingWatches("newProp"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -132,9 +154,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test observed with track transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts index a1e60b0fac2f4c912322eaa339453d70a5a0e624..574046a3cac56c5c34670df15b3c439b67871b5e 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,22 @@ const observedTrackTransform: Plugins = { const pluginTester = new PluginTester('test track only transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; @@ -67,8 +65,8 @@ function main() {} -class C implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +class C implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -82,48 +80,55 @@ class C implements IObservedObject { this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propC: number = 1; - private __backing_trackC: number = 2; + @JSONRename({newName:"trackC"}) private __backing_trackC: number = 2; - private __meta_trackC: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackC: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackC(): number { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackC.addRef(); - } + this.conditionalAddRef(this.__meta_trackC); return this.__backing_trackC; } public set trackC(newValue: number) { if (((this.__backing_trackC) !== (newValue))) { this.__backing_trackC = newValue; - this.__meta_trackC.fireChange(); - this.executeOnSubscribingWatches("trackC"); + this.__meta_trackC.fireChange(); + this.executeOnSubscribingWatches("trackC"); } } } -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } - `; function testObservedOnlyTransformer(this: PluginTestContext): void { @@ -132,9 +137,9 @@ function testObservedOnlyTransformer(this: PluginTestContext): void { pluginTester.run( 'test track only transform', - [observedTrackTransform, uiNoRecheck], + [observedTrackTransform, uiNoRecheck, recheck], { - checked: [testObservedOnlyTransformer], + 'checked:ui-no-recheck': [testObservedOnlyTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2ce9d195def6d50a7dfad20950be105dd834f9c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/base-observedv2-trace.test.ts @@ -0,0 +1,182 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'base-observedv2-trace.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic @ObservedV2 and @Trace case', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class CC implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceB"}) private __backing_traceB: number = 2; + + @JSONStringifyIgnore() private __meta_traceB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceB(): number { + this.conditionalAddRef(this.__meta_traceB); + return UIUtils.makeObserved(this.__backing_traceB); + } + + public set traceB(newValue: number) { + if (((this.__backing_traceB) !== (newValue))) { + this.__backing_traceB = newValue; + this.__meta_traceB.fireChange(); + this.executeOnSubscribingWatches("traceB"); + } + } + + public constructor() {} + +} + +@ObservedV2() class DD implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceE"}) private __backing_traceE: number = 2; + + @JSONStringifyIgnore() private __meta_traceE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"tracef"}) private __backing_tracef: number = 2; + + @JSONStringifyIgnore() private __meta_tracef: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public vv: string; + + public get traceE(): number { + this.conditionalAddRef(this.__meta_traceE); + return UIUtils.makeObserved(this.__backing_traceE); + } + + public set traceE(newValue: number) { + if (((this.__backing_traceE) !== (newValue))) { + this.__backing_traceE = newValue; + this.__meta_traceE.fireChange(); + this.executeOnSubscribingWatches("traceE"); + } + } + + public get tracef(): number { + this.conditionalAddRef(this.__meta_tracef); + return UIUtils.makeObserved(this.__backing_tracef); + } + + public set tracef(newValue: number) { + if (((this.__backing_tracef) !== (newValue))) { + this.__backing_tracef = newValue; + this.__meta_tracef.fireChange(); + this.executeOnSubscribingWatches("tracef"); + } + } + + public constructor(vv1: string) { + this.vv = vv1; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic @ObservedV2 and @Trace case', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4091bd26deec6b5a3c0f824c6baf57c4f85234e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observed-serialization.test.ts @@ -0,0 +1,277 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-serialization.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 class serialization', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Retention({policy:"SOURCE"}) @interface TestDecor {} + +@ObservedV2() class testJSONStringifyIgnore implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONStringifyIgnore() private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONStringifyIgnore() @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return UIUtils.makeObserved(this.__backing_var2); + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return UIUtils.makeObserved(this.__backing_var5); + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return UIUtils.makeObserved(this.__backing_var7); + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return UIUtils.makeObserved(this.__backing_var8); + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + + public constructor() {} + +} + +@ObservedV2() class testJsonRename implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public var1: number = 1; + + @JSONRename({newName:"var2"}) private __backing_var2: number = 2; + + @JSONStringifyIgnore() private __meta_var2: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name3"}) public var3: number = 3; + + @TestDecor() public var4: number = 4; + + @JSONRename({value:"name5"}) private __backing_var5: number = 5; + + @JSONStringifyIgnore() private __meta_var5: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name6"}) @TestDecor() public var6: number = 6; + + @TestDecor() @JSONRename({newName:"var7"}) private __backing_var7: number = 7; + + @JSONStringifyIgnore() private __meta_var7: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({value:"name8"}) @TestDecor() private __backing_var8: number = 8; + + @JSONStringifyIgnore() private __meta_var8: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get var2(): number { + this.conditionalAddRef(this.__meta_var2); + return UIUtils.makeObserved(this.__backing_var2); + } + + public set var2(newValue: number) { + if (((this.__backing_var2) !== (newValue))) { + this.__backing_var2 = newValue; + this.__meta_var2.fireChange(); + this.executeOnSubscribingWatches("var2"); + } + } + + public get var5(): number { + this.conditionalAddRef(this.__meta_var5); + return UIUtils.makeObserved(this.__backing_var5); + } + + public set var5(newValue: number) { + if (((this.__backing_var5) !== (newValue))) { + this.__backing_var5 = newValue; + this.__meta_var5.fireChange(); + this.executeOnSubscribingWatches("var5"); + } + } + + public get var7(): number { + this.conditionalAddRef(this.__meta_var7); + return UIUtils.makeObserved(this.__backing_var7); + } + + public set var7(newValue: number) { + if (((this.__backing_var7) !== (newValue))) { + this.__backing_var7 = newValue; + this.__meta_var7.fireChange(); + this.executeOnSubscribingWatches("var7"); + } + } + + public get var8(): number { + this.conditionalAddRef(this.__meta_var8); + return UIUtils.makeObserved(this.__backing_var8); + } + + public set var8(newValue: number) { + if (((this.__backing_var8) !== (newValue))) { + this.__backing_var8 = newValue; + this.__meta_var8.fireChange(); + this.executeOnSubscribingWatches("var8"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 class serialization', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f77d52bc09853a5ad8b4304d5a0b9c6975e98a35 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-extends.test.ts @@ -0,0 +1,166 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-extends.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 extends class', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propA: number = 1; + + @JSONRename({newName:"traceA"}) private __backing_traceA: number = 2; + + @JSONStringifyIgnore() private __meta_traceA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceA(): number { + this.conditionalAddRef(this.__meta_traceA); + return UIUtils.makeObserved(this.__backing_traceA); + } + + public set traceA(newValue: number) { + if (((this.__backing_traceA) !== (newValue))) { + this.__backing_traceA = newValue; + this.__meta_traceA.fireChange(); + this.executeOnSubscribingWatches("traceA"); + } + } + + public constructor() {} + +} + +class G extends A { + public propG: number = 1; + + public constructor() {} + +} + +@ObservedV2() class H extends G implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"propG"}) private __backing_propG: number = 1; + + @JSONStringifyIgnore() private __meta_propG: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get propG(): number { + this.conditionalAddRef(this.__meta_propG); + return UIUtils.makeObserved(this.__backing_propG); + } + + public set propG(newValue: number) { + if (((this.__backing_propG) !== (newValue))) { + this.__backing_propG = newValue; + this.__meta_propG.fireChange(); + this.executeOnSubscribingWatches("propG"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 extends class', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1e2137ce9dac034dcbd9cead570c543c126880c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-implements.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-implements.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 implements interface', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +interface PropInterface { + set propF(propF: number) + + get propF(): number + +} + +interface trackInterface { + set trackF(trackF: number) + + get trackF(): number + +} + +@ObservedV2() class F implements PropInterface, trackInterface, IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"bb"}) private __backing_bb?: boolean; + + @JSONStringifyIgnore() private __meta_bb: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"propF"}) private __backing_propF: number = 1; + + @JSONStringifyIgnore() private __meta_propF: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + private _$property$_trackF: number = 2; + + set propF(newValue: number) { + if (((this.__backing_propF) !== (newValue))) { + this.__backing_propF = newValue; + this.__meta_propF.fireChange(); + this.executeOnSubscribingWatches("propF"); + } + } + + public get propF(): number { + this.conditionalAddRef(this.__meta_propF); + return UIUtils.makeObserved(this.__backing_propF); + } + + set trackF(_$property$_trackF: number) { + this._$property$_trackF = _$property$_trackF; + return; + } + + public get trackF(): number { + return this._$property$_trackF; + } + + public get bb(): boolean { + this.conditionalAddRef(this.__meta_bb); + return UIUtils.makeObserved((this.__backing_bb as boolean)); + } + + public set bb(newValue: boolean) { + if (((this.__backing_bb) !== (newValue))) { + this.__backing_bb = newValue; + this.__meta_bb.fireChange(); + this.executeOnSubscribingWatches("bb"); + } + } + + public constructor(bb: boolean) { + this.bb = bb; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 implements interface', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..11b567e4f18cadcd511e2e5bdf067913c0cf2466 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/observedv2-trace-types.test.ts @@ -0,0 +1,380 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'observedv2-trace-types.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @ObservedV2 class with different types class property', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Person { + public constructor() {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@ObservedV2() class mixed1 implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"numA"}) private __backing_numA: number = 33; + + @JSONStringifyIgnore() private __meta_numA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; + + @JSONStringifyIgnore() private __meta_stringA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; + + @JSONStringifyIgnore() private __meta_booleanA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; + + @JSONStringifyIgnore() private __meta_arrayA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; + + @JSONStringifyIgnore() private __meta_objectA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); + + @JSONStringifyIgnore() private __meta_dateA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); + + @JSONStringifyIgnore() private __meta_setA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); + + @JSONStringifyIgnore() private __meta_mapA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; + + @JSONStringifyIgnore() private __meta_unionA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); + + @JSONStringifyIgnore() private __meta_classA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + @JSONStringifyIgnore() private __meta_enumA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public numB: number = 33; + + public stringB: string = "AA"; + + public booleanB: boolean = true; + + public arrayB: Array = [1, 2, 3]; + + public objectB: Object = {}; + + public dateB: Date = new Date("2021-08-08"); + + public setB: Set = new Set(); + + public mapB: Map = new Map(); + + public unionB: (string | undefined) = ""; + + public classB: Person = new Person(); + + public enumB: Status = Status.NotFound; + + public get numA(): number { + this.conditionalAddRef(this.__meta_numA); + return UIUtils.makeObserved(this.__backing_numA); + } + + public set numA(newValue: number) { + if (((this.__backing_numA) !== (newValue))) { + this.__backing_numA = newValue; + this.__meta_numA.fireChange(); + this.executeOnSubscribingWatches("numA"); + } + } + + public get stringA(): string { + this.conditionalAddRef(this.__meta_stringA); + return UIUtils.makeObserved(this.__backing_stringA); + } + + public set stringA(newValue: string) { + if (((this.__backing_stringA) !== (newValue))) { + this.__backing_stringA = newValue; + this.__meta_stringA.fireChange(); + this.executeOnSubscribingWatches("stringA"); + } + } + + public get booleanA(): boolean { + this.conditionalAddRef(this.__meta_booleanA); + return UIUtils.makeObserved(this.__backing_booleanA); + } + + public set booleanA(newValue: boolean) { + if (((this.__backing_booleanA) !== (newValue))) { + this.__backing_booleanA = newValue; + this.__meta_booleanA.fireChange(); + this.executeOnSubscribingWatches("booleanA"); + } + } + + public get arrayA(): Array { + this.conditionalAddRef(this.__meta_arrayA); + return UIUtils.makeObserved(this.__backing_arrayA); + } + + public set arrayA(newValue: Array) { + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta_arrayA.fireChange(); + this.executeOnSubscribingWatches("arrayA"); + } + } + + public get objectA(): Object { + this.conditionalAddRef(this.__meta_objectA); + return UIUtils.makeObserved(this.__backing_objectA); + } + + public set objectA(newValue: Object) { + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta_objectA.fireChange(); + this.executeOnSubscribingWatches("objectA"); + } + } + + public get dateA(): Date { + this.conditionalAddRef(this.__meta_dateA); + return UIUtils.makeObserved(this.__backing_dateA); + } + + public set dateA(newValue: Date) { + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta_dateA.fireChange(); + this.executeOnSubscribingWatches("dateA"); + } + } + + public get setA(): Set { + this.conditionalAddRef(this.__meta_setA); + return UIUtils.makeObserved(this.__backing_setA); + } + + public set setA(newValue: Set) { + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta_setA.fireChange(); + this.executeOnSubscribingWatches("setA"); + } + } + + public get mapA(): Map { + this.conditionalAddRef(this.__meta_mapA); + return UIUtils.makeObserved(this.__backing_mapA); + } + + public set mapA(newValue: Map) { + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta_mapA.fireChange(); + this.executeOnSubscribingWatches("mapA"); + } + } + + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta_unionA); + return UIUtils.makeObserved(this.__backing_unionA); + } + + public set unionA(newValue: (string | undefined)) { + if (((this.__backing_unionA) !== (newValue))) { + this.__backing_unionA = newValue; + this.__meta_unionA.fireChange(); + this.executeOnSubscribingWatches("unionA"); + } + } + + public get classA(): Person { + this.conditionalAddRef(this.__meta_classA); + return UIUtils.makeObserved(this.__backing_classA); + } + + public set classA(newValue: Person) { + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta_classA.fireChange(); + this.executeOnSubscribingWatches("classA"); + } + } + + public get enumA(): Status { + this.conditionalAddRef(this.__meta_enumA); + return UIUtils.makeObserved(this.__backing_enumA); + } + + public set enumA(newValue: Status) { + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @ObservedV2 class with different types class property', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..5205a28ae0b88d4777d4e8bd55fe941f097a452e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/static-observedv2-trace.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'static-observedv2-trace.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test static @Trace variable case', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class CC implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + public static propA: number = 1; + + @JSONRename({newName:"traceB"}) public static __backing_traceB: number = 2; + + @JSONStringifyIgnore() public static __meta_traceB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + static { + + } + public static get traceB(): number { + CC.__meta_traceB.addRef(); + return UIUtils.makeObserved(CC.__backing_traceB); + } + + public static set traceB(newValue: number) { + if (((CC.__backing_traceB) !== (newValue))) { + CC.__backing_traceB = newValue; + CC.__meta_traceB.fireChange(); + } + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test static @Trace variable case', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..facbf02cf8a72522e18982f520aaf9b5d5c7d1ee --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observedv2-trace/trace-with-constructor.test.ts @@ -0,0 +1,136 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/observedv2-trace'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'trace-with-constructor.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic @Trace decorated variable initialized with constuctor', buildConfig); + +const expectedScript: string = ` +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ObservedV2() class FF implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + public propB: number = 1; + + @JSONRename({newName:"traceE"}) private __backing_traceE: number = 2; + + @JSONStringifyIgnore() private __meta_traceE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"vv"}) private __backing_vv?: string; + + @JSONStringifyIgnore() private __meta_vv: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get traceE(): number { + this.conditionalAddRef(this.__meta_traceE); + return UIUtils.makeObserved(this.__backing_traceE); + } + + public set traceE(newValue: number) { + if (((this.__backing_traceE) !== (newValue))) { + this.__backing_traceE = newValue; + this.__meta_traceE.fireChange(); + this.executeOnSubscribingWatches("traceE"); + } + } + + public get vv(): string { + this.conditionalAddRef(this.__meta_vv); + return UIUtils.makeObserved((this.__backing_vv as string)); + } + + public set vv(newValue: string) { + if (((this.__backing_vv) !== (newValue))) { + this.__backing_vv = newValue; + this.__meta_vv.fireChange(); + this.executeOnSubscribingWatches("vv"); + } + } + + public constructor(vv1: string) { + this.vv = vv1; + } + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic @Trace decorated variable initialized with constuctor', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b2e68cca511a8e53f8603a9794b02441a9a6cba --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-basic-type.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-basic-type.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic type @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceVar1 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar1", ((({let gensym___35048285 = initializers; + (((gensym___35048285) == (null)) ? undefined : gensym___35048285.onceVar1)})) ?? ("stateVar1"))); + this.__backing_onceVar2 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar2", ((({let gensym___241100287 = initializers; + (((gensym___241100287) == (null)) ? undefined : gensym___241100287.onceVar2)})) ?? (50))); + this.__backing_onceVar3 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar3", ((({let gensym___114153851 = initializers; + (((gensym___114153851) == (null)) ? undefined : gensym___114153851.onceVar3)})) ?? (true))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar4", ((({let gensym___18636164 = initializers; + (((gensym___18636164) == (null)) ? undefined : gensym___18636164.onceVar4)})) ?? (undefined))); + this.__backing_onceVar5 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar5", ((({let gensym___60128231 = initializers; + (((gensym___60128231) == (null)) ? undefined : gensym___60128231.onceVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_onceVar1?: IParamOnceDecoratedVariable; + + public get onceVar1(): string { + return this.__backing_onceVar1!.get(); + } + + public set onceVar1(value: string) { + this.__backing_onceVar1!.set(value); + } + + private __backing_onceVar2?: IParamOnceDecoratedVariable; + + public get onceVar2(): number { + return this.__backing_onceVar2!.get(); + } + + public set onceVar2(value: number) { + this.__backing_onceVar2!.set(value); + } + + private __backing_onceVar3?: IParamOnceDecoratedVariable; + + public get onceVar3(): boolean { + return this.__backing_onceVar3!.get(); + } + + public set onceVar3(value: boolean) { + this.__backing_onceVar3!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable; + + public get onceVar4(): undefined { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: undefined) { + this.__backing_onceVar4!.set(value); + } + + private __backing_onceVar5?: IParamOnceDecoratedVariable; + + public get onceVar5(): null { + return this.__backing_onceVar5!.get(); + } + + public set onceVar5(value: null) { + this.__backing_onceVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set onceVar1(onceVar1: (string | undefined)) + + get onceVar1(): (string | undefined) + @Param() set __backing_onceVar1(__backing_onceVar1: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar1(): (IParamOnceDecoratedVariable | undefined) + set onceVar2(onceVar2: (number | undefined)) + + get onceVar2(): (number | undefined) + @Param() set __backing_onceVar2(__backing_onceVar2: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar2(): (IParamOnceDecoratedVariable | undefined) + set onceVar3(onceVar3: (boolean | undefined)) + + get onceVar3(): (boolean | undefined) + @Param() set __backing_onceVar3(__backing_onceVar3: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar3(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (undefined | undefined)) + + get onceVar4(): (undefined | undefined) + @Param() set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar4(): (IParamOnceDecoratedVariable | undefined) + set onceVar5(onceVar5: (null | undefined)) + + get onceVar5(): (null | undefined) + @Param() set __backing_onceVar5(__backing_onceVar5: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar5(): (IParamOnceDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Once decorated variables transformation', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e044f9ed1d6b0a309e196ace8da84e83512b4b22 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-complex-type.test.ts @@ -0,0 +1,378 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-complex-type.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test complex type @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceVar1 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar1", ((({let gensym___35048285 = initializers; + (((gensym___35048285) == (null)) ? undefined : gensym___35048285.onceVar1)})) ?? (new Per(6)))); + this.__backing_onceVar2 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar2", ((({let gensym___241100287 = initializers; + (((gensym___241100287) == (null)) ? undefined : gensym___241100287.onceVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_onceVar3 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar3", ((({let gensym___114153851 = initializers; + (((gensym___114153851) == (null)) ? undefined : gensym___114153851.onceVar3)})) ?? (StateType.TYPE3))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar4", ((({let gensym___18636164 = initializers; + (((gensym___18636164) == (null)) ? undefined : gensym___18636164.onceVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_onceVar5 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar5", ((({let gensym___60128231 = initializers; + (((gensym___60128231) == (null)) ? undefined : gensym___60128231.onceVar5)})) ?? ([true, false]))); + this.__backing_onceVar6 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar6", ((({let gensym___266500361 = initializers; + (((gensym___266500361) == (null)) ? undefined : gensym___266500361.onceVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_onceVar7 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar7", ((({let gensym___265659510 = initializers; + (((gensym___265659510) == (null)) ? undefined : gensym___265659510.onceVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_onceVar8 = STATE_MGMT_FACTORY.makeParamOnce<((sr: string)=> void)>(this, "onceVar8", ((({let gensym___258402603 = initializers; + (((gensym___258402603) == (null)) ? undefined : gensym___258402603.onceVar8)})) ?? (((sr: string) => {})))); + this.__backing_onceVar9 = STATE_MGMT_FACTORY.makeParamOnce(this, "onceVar9", ((({let gensym___90206878 = initializers; + (((gensym___90206878) == (null)) ? undefined : gensym___90206878.onceVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_onceVar10 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar10", ((({let gensym___76706777 = initializers; + (((gensym___76706777) == (null)) ? undefined : gensym___76706777.onceVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_onceVar11 = STATE_MGMT_FACTORY.makeParamOnce<(string | number)>(this, "onceVar11", ((({let gensym___188373395 = initializers; + (((gensym___188373395) == (null)) ? undefined : gensym___188373395.onceVar11)})) ?? (0.0))); + this.__backing_onceVar12 = STATE_MGMT_FACTORY.makeParamOnce<(Set | Per)>(this, "onceVar12", ((({let gensym___207702762 = initializers; + (((gensym___207702762) == (null)) ? undefined : gensym___207702762.onceVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_onceVar1?: IParamOnceDecoratedVariable; + + public get onceVar1(): Per { + return this.__backing_onceVar1!.get(); + } + + public set onceVar1(value: Per) { + this.__backing_onceVar1!.set(value); + } + + private __backing_onceVar2?: IParamOnceDecoratedVariable>; + + public get onceVar2(): Array { + return this.__backing_onceVar2!.get(); + } + + public set onceVar2(value: Array) { + this.__backing_onceVar2!.set(value); + } + + private __backing_onceVar3?: IParamOnceDecoratedVariable; + + public get onceVar3(): StateType { + return this.__backing_onceVar3!.get(); + } + + public set onceVar3(value: StateType) { + this.__backing_onceVar3!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable>; + + public get onceVar4(): Set { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: Set) { + this.__backing_onceVar4!.set(value); + } + + private __backing_onceVar5?: IParamOnceDecoratedVariable>; + + public get onceVar5(): Array { + return this.__backing_onceVar5!.get(); + } + + public set onceVar5(value: Array) { + this.__backing_onceVar5!.set(value); + } + + private __backing_onceVar6?: IParamOnceDecoratedVariable>; + + public get onceVar6(): Array { + return this.__backing_onceVar6!.get(); + } + + public set onceVar6(value: Array) { + this.__backing_onceVar6!.set(value); + } + + private __backing_onceVar7?: IParamOnceDecoratedVariable>; + + public get onceVar7(): Array { + return this.__backing_onceVar7!.get(); + } + + public set onceVar7(value: Array) { + this.__backing_onceVar7!.set(value); + } + + private __backing_onceVar8?: IParamOnceDecoratedVariable<((sr: string)=> void)>; + + public get onceVar8(): ((sr: string)=> void) { + return this.__backing_onceVar8!.get(); + } + + public set onceVar8(value: ((sr: string)=> void)) { + this.__backing_onceVar8!.set(value); + } + + private __backing_onceVar9?: IParamOnceDecoratedVariable; + + public get onceVar9(): Date { + return this.__backing_onceVar9!.get(); + } + + public set onceVar9(value: Date) { + this.__backing_onceVar9!.set(value); + } + + private __backing_onceVar10?: IParamOnceDecoratedVariable>; + + public get onceVar10(): Map { + return this.__backing_onceVar10!.get(); + } + + public set onceVar10(value: Map) { + this.__backing_onceVar10!.set(value); + } + + private __backing_onceVar11?: IParamOnceDecoratedVariable<(string | number)>; + + public get onceVar11(): (string | number) { + return this.__backing_onceVar11!.get(); + } + + public set onceVar11(value: (string | number)) { + this.__backing_onceVar11!.set(value); + } + + private __backing_onceVar12?: IParamOnceDecoratedVariable<(Set | Per)>; + + public get onceVar12(): (Set | Per) { + return this.__backing_onceVar12!.get(); + } + + public set onceVar12(value: (Set | Per)) { + this.__backing_onceVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set onceVar1(onceVar1: (Per | undefined)) + + get onceVar1(): (Per | undefined) + @Param() set __backing_onceVar1(__backing_onceVar1: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar1(): (IParamOnceDecoratedVariable | undefined) + set onceVar2(onceVar2: (Array | undefined)) + + get onceVar2(): (Array | undefined) + @Param() set __backing_onceVar2(__backing_onceVar2: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar2(): (IParamOnceDecoratedVariable> | undefined) + set onceVar3(onceVar3: (StateType | undefined)) + + get onceVar3(): (StateType | undefined) + @Param() set __backing_onceVar3(__backing_onceVar3: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar3(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (Set | undefined)) + + get onceVar4(): (Set | undefined) + @Param() set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar4(): (IParamOnceDecoratedVariable> | undefined) + set onceVar5(onceVar5: (Array | undefined)) + + get onceVar5(): (Array | undefined) + @Param() set __backing_onceVar5(__backing_onceVar5: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar5(): (IParamOnceDecoratedVariable> | undefined) + set onceVar6(onceVar6: (Array | undefined)) + + get onceVar6(): (Array | undefined) + @Param() set __backing_onceVar6(__backing_onceVar6: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar6(): (IParamOnceDecoratedVariable> | undefined) + set onceVar7(onceVar7: (Array | undefined)) + + get onceVar7(): (Array | undefined) + @Param() set __backing_onceVar7(__backing_onceVar7: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar7(): (IParamOnceDecoratedVariable> | undefined) + set onceVar8(onceVar8: (((sr: string)=> void) | undefined)) + + get onceVar8(): (((sr: string)=> void) | undefined) + @Param() set __backing_onceVar8(__backing_onceVar8: (IParamOnceDecoratedVariable<((sr: string)=> void)> | undefined)) + + @Param() get __backing_onceVar8(): (IParamOnceDecoratedVariable<((sr: string)=> void)> | undefined) + set onceVar9(onceVar9: (Date | undefined)) + + get onceVar9(): (Date | undefined) + @Param() set __backing_onceVar9(__backing_onceVar9: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceVar9(): (IParamOnceDecoratedVariable | undefined) + set onceVar10(onceVar10: (Map | undefined)) + + get onceVar10(): (Map | undefined) + @Param() set __backing_onceVar10(__backing_onceVar10: (IParamOnceDecoratedVariable> | undefined)) + + @Param() get __backing_onceVar10(): (IParamOnceDecoratedVariable> | undefined) + set onceVar11(onceVar11: ((string | number) | undefined)) + + get onceVar11(): ((string | number) | undefined) + @Param() set __backing_onceVar11(__backing_onceVar11: (IParamOnceDecoratedVariable<(string | number)> | undefined)) + + @Param() get __backing_onceVar11(): (IParamOnceDecoratedVariable<(string | number)> | undefined) + set onceVar12(onceVar12: ((Set | Per) | undefined)) + + get onceVar12(): ((Set | Per) | undefined) + @Param() set __backing_onceVar12(__backing_onceVar12: (IParamOnceDecoratedVariable<(Set | Per)> | undefined)) + + @Param() get __backing_onceVar12(): (IParamOnceDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Once decorated variables transformation', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..db04b7b652004f4aaf63e51d0c3ae4466c84251d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-only.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-only.ets'), +]; + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test only @Once decorated variables transformation', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; +import { Once as Once } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceParamNum = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamNum", ((({let gensym___118919021 = initializers; + (((gensym___118919021) == (null)) ? undefined : gensym___118919021.onceParamNum)})) ?? (0))); + this.__backing_onceVar4 = STATE_MGMT_FACTORY.makeParamOnce>(this, "onceVar4", ((({let gensym___71001521 = initializers; + (((gensym___71001521) == (null)) ? undefined : gensym___71001521.onceVar4)})) ?? (new Set(new Array("aa", "bb"))))); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_onceParamNum?: IParamOnceDecoratedVariable; + + public get onceParamNum(): number { + return this.__backing_onceParamNum!.get(); + } + + public set onceParamNum(value: number) { + this.__backing_onceParamNum!.set(value); + } + + private __backing_onceVar4?: IParamOnceDecoratedVariable>; + + public get onceVar4(): Set { + return this.__backing_onceVar4!.get(); + } + + public set onceVar4(value: Set) { + this.__backing_onceVar4!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set onceParamNum(onceParamNum: (number | undefined)) + + get onceParamNum(): (number | undefined) + set __backing_onceParamNum(__backing_onceParamNum: (IParamOnceDecoratedVariable | undefined)) + + get __backing_onceParamNum(): (IParamOnceDecoratedVariable | undefined) + set onceVar4(onceVar4: (Set | undefined)) + + get onceVar4(): (Set | undefined) + set __backing_onceVar4(__backing_onceVar4: (IParamOnceDecoratedVariable> | undefined)) + + get __backing_onceVar4(): (IParamOnceDecoratedVariable> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test only @Once decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9dee701f476bbabd59ea7bdcaa52e603e7748f73 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/once/once-with-require.test.ts @@ -0,0 +1,327 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/once'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'once-with-require.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @Once Decorator with @Require', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text, Button as Button } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once, ObservedV2 as ObservedV2, Trace as Trace, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +@ObservedV2() class Info { + @Trace() public name: string = "info"; + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + @Param() @Once() public onceParamNum: number = 0; + + @Param() @Once() @Require() public onceParamInfo!: Info; + + public build() { + Column(){ + Text(\`Child onceParamNum: \${this.onceParamNum}\`); + Text(\`Child onceParamInfo: \${this.onceParamInfo.name}\`); + Button("changeOnceParamNum").onClick(((e) => { + (this.onceParamNum++); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public localNum: number = 10; + + @Local() public localInfo: Info = new Info(); + + public build() { + Column(){ + Text(\`Parent localNum: \${this.localNum}\`); + Text(\`Parent localInfo: \${this.localInfo.name}\`); + Child({ + onceParamNum: this.localNum, + onceParamInfo: this.localInfo, + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + onceParamNum?: number; + @Param() @Once() __backing_onceParamNum?: number; + onceParamInfo?: Info; + @Param() @Once() @Require() __backing_onceParamInfo?: Info; + +} + +@ComponentV2() export interface __Options_Index { + localNum?: number; + @Local() __backing_localNum?: number; + localInfo?: Info; + @Local() __backing_localInfo?: Info; + +} +`; + +const expectedCheckedScript: string = ` +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IParamOnceDecoratedVariable as IParamOnceDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, Text as Text, Button as Button } from "@ohos.arkui.component"; + +import { Param as Param, Once as Once, ObservedV2 as ObservedV2, Trace as Trace, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ObservedV2() class Info implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name: string = "info"; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved(this.__backing_name); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_onceParamNum = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamNum", ((({let gensym___118919021 = initializers; + (((gensym___118919021) == (null)) ? undefined : gensym___118919021.onceParamNum)})) ?? (0))); + this.__backing_onceParamInfo = STATE_MGMT_FACTORY.makeParamOnce(this, "onceParamInfo", (initializers!.onceParamInfo as Info)); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_onceParamNum?: IParamOnceDecoratedVariable; + + public get onceParamNum(): number { + return this.__backing_onceParamNum!.get(); + } + + public set onceParamNum(value: number) { + this.__backing_onceParamNum!.set(value); + } + + private __backing_onceParamInfo?: IParamOnceDecoratedVariable; + + public get onceParamInfo(): Info { + return this.__backing_onceParamInfo!.get(); + } + + public set onceParamInfo(value: Info) { + this.__backing_onceParamInfo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Child onceParamNum: \${this.onceParamNum}\`, undefined, undefined); + Text(undefined, \`Child onceParamInfo: \${this.onceParamInfo.name}\`, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.onceParamNum++); + })); + return; + }), "changeOnceParamNum", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_localNum = STATE_MGMT_FACTORY.makeLocal(this, "localNum", 10); + this.__backing_localInfo = STATE_MGMT_FACTORY.makeLocal(this, "localInfo", new Info()); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_localNum?: ILocalDecoratedVariable; + + public get localNum(): number { + return this.__backing_localNum!.get(); + } + + public set localNum(value: number) { + this.__backing_localNum!.set(value); + } + + private __backing_localInfo?: ILocalDecoratedVariable; + + public get localInfo(): Info { + return this.__backing_localInfo!.get(); + } + + public set localInfo(value: Info) { + this.__backing_localInfo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Parent localNum: \${this.localNum}\`, undefined, undefined); + Text(undefined, \`Parent localInfo: \${this.localInfo.name}\`, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + onceParamNum: this.localNum, + onceParamInfo: this.localInfo, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Child { + set onceParamNum(onceParamNum: (number | undefined)) + + get onceParamNum(): (number | undefined) + @Param() set __backing_onceParamNum(__backing_onceParamNum: (IParamOnceDecoratedVariable | undefined)) + + @Param() get __backing_onceParamNum(): (IParamOnceDecoratedVariable | undefined) + set onceParamInfo(onceParamInfo: (Info | undefined)) + + get onceParamInfo(): (Info | undefined) + @Param() @Require() set __backing_onceParamInfo(__backing_onceParamInfo: (IParamOnceDecoratedVariable | undefined)) + + @Param() @Require() get __backing_onceParamInfo(): (IParamOnceDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_Index { + set localNum(localNum: (number | undefined)) + + get localNum(): (number | undefined) + set __backing_localNum(__backing_localNum: (ILocalDecoratedVariable | undefined)) + + get __backing_localNum(): (ILocalDecoratedVariable | undefined) + set localInfo(localInfo: (Info | undefined)) + + get localInfo(): (Info | undefined) + set __backing_localInfo(__backing_localInfo: (ILocalDecoratedVariable | undefined)) + + get __backing_localInfo(): (ILocalDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Once Decorator with @Require', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c02112576e43d7a643f5bd999cca5e60617b87be --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-basic-type.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'param-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Param decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeParam(this, "paramVar1", ((({let gensym___264789668 = initializers; + (((gensym___264789668) == (null)) ? undefined : gensym___264789668.paramVar1)})) ?? ("stateVar1"))); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeParam(this, "paramVar2", ((({let gensym___171906071 = initializers; + (((gensym___171906071) == (null)) ? undefined : gensym___171906071.paramVar2)})) ?? (50))); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeParam(this, "paramVar3", ((({let gensym___241535547 = initializers; + (((gensym___241535547) == (null)) ? undefined : gensym___241535547.paramVar3)})) ?? (true))); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeParam(this, "paramVar4", ((({let gensym___49490075 = initializers; + (((gensym___49490075) == (null)) ? undefined : gensym___49490075.paramVar4)})) ?? (undefined))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeParam(this, "paramVar5", ((({let gensym___17164613 = initializers; + (((gensym___17164613) == (null)) ? undefined : gensym___17164613.paramVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___99629634 = initializers; + (((gensym___99629634) == (null)) ? undefined : gensym___99629634.paramVar1)})) !== (undefined))) { + this.__backing_paramVar1!.update((initializers!.paramVar1 as string)); + } + if (((({let gensym___68516859 = initializers; + (((gensym___68516859) == (null)) ? undefined : gensym___68516859.paramVar2)})) !== (undefined))) { + this.__backing_paramVar2!.update((initializers!.paramVar2 as number)); + } + if (((({let gensym___96937083 = initializers; + (((gensym___96937083) == (null)) ? undefined : gensym___96937083.paramVar3)})) !== (undefined))) { + this.__backing_paramVar3!.update((initializers!.paramVar3 as boolean)); + } + if (((({let gensym___151087626 = initializers; + (((gensym___151087626) == (null)) ? undefined : gensym___151087626.paramVar4)})) !== (undefined))) { + this.__backing_paramVar4!.update((initializers!.paramVar4 as undefined)); + } + if (((({let gensym___127163363 = initializers; + (((gensym___127163363) == (null)) ? undefined : gensym___127163363.paramVar5)})) !== (undefined))) { + this.__backing_paramVar5!.update((initializers!.paramVar5 as null)); + } + } + + private __backing_paramVar1?: IParamDecoratedVariable; + + public get paramVar1(): string { + return this.__backing_paramVar1!.get(); + } + + private __backing_paramVar2?: IParamDecoratedVariable; + + public get paramVar2(): number { + return this.__backing_paramVar2!.get(); + } + + private __backing_paramVar3?: IParamDecoratedVariable; + + public get paramVar3(): boolean { + return this.__backing_paramVar3!.get(); + } + + private __backing_paramVar4?: IParamDecoratedVariable; + + public get paramVar4(): undefined { + return this.__backing_paramVar4!.get(); + } + + private __backing_paramVar5?: IParamDecoratedVariable; + + public get paramVar5(): null { + return this.__backing_paramVar5!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (string | undefined)) + + get paramVar1(): (string | undefined) + set __backing_paramVar1(__backing_paramVar1: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IParamDecoratedVariable | undefined) + set paramVar2(paramVar2: (number | undefined)) + + get paramVar2(): (number | undefined) + set __backing_paramVar2(__backing_paramVar2: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar2(): (IParamDecoratedVariable | undefined) + set paramVar3(paramVar3: (boolean | undefined)) + + get paramVar3(): (boolean | undefined) + set __backing_paramVar3(__backing_paramVar3: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IParamDecoratedVariable | undefined) + set paramVar4(paramVar4: (undefined | undefined)) + + get paramVar4(): (undefined | undefined) + set __backing_paramVar4(__backing_paramVar4: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar4(): (IParamDecoratedVariable | undefined) + set paramVar5(paramVar5: (null | undefined)) + + get paramVar5(): (null | undefined) + set __backing_paramVar5(__backing_paramVar5: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar5(): (IParamDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Param decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3639b125344f3716fc557533668f6dafb7d1a3e3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-complex-type.test.ts @@ -0,0 +1,381 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'param-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Param decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeParam(this, "paramVar1", ((({let gensym___264789668 = initializers; + (((gensym___264789668) == (null)) ? undefined : gensym___264789668.paramVar1)})) ?? (new Per(6)))); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar2", ((({let gensym___171906071 = initializers; + (((gensym___171906071) == (null)) ? undefined : gensym___171906071.paramVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeParam(this, "paramVar3", ((({let gensym___241535547 = initializers; + (((gensym___241535547) == (null)) ? undefined : gensym___241535547.paramVar3)})) ?? (StateType.TYPE3))); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar4", ((({let gensym___49490075 = initializers; + (((gensym___49490075) == (null)) ? undefined : gensym___49490075.paramVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar5", ((({let gensym___17164613 = initializers; + (((gensym___17164613) == (null)) ? undefined : gensym___17164613.paramVar5)})) ?? ([true, false]))); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar6", ((({let gensym___84871771 = initializers; + (((gensym___84871771) == (null)) ? undefined : gensym___84871771.paramVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar7", ((({let gensym___263986833 = initializers; + (((gensym___263986833) == (null)) ? undefined : gensym___263986833.paramVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_paramVar8 = STATE_MGMT_FACTORY.makeParam<((sr: string)=> void)>(this, "paramVar8", ((({let gensym___6968121 = initializers; + (((gensym___6968121) == (null)) ? undefined : gensym___6968121.paramVar8)})) ?? (((sr: string) => {})))); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeParam(this, "paramVar9", ((({let gensym___63984493 = initializers; + (((gensym___63984493) == (null)) ? undefined : gensym___63984493.paramVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeParam>(this, "paramVar10", ((({let gensym___260234648 = initializers; + (((gensym___260234648) == (null)) ? undefined : gensym___260234648.paramVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeParam<(string | number)>(this, "paramVar11", ((({let gensym___144998584 = initializers; + (((gensym___144998584) == (null)) ? undefined : gensym___144998584.paramVar11)})) ?? (0.0))); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeParam<(Set | Per)>(this, "paramVar12", ((({let gensym___237878674 = initializers; + (((gensym___237878674) == (null)) ? undefined : gensym___237878674.paramVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___99629634 = initializers; + (((gensym___99629634) == (null)) ? undefined : gensym___99629634.paramVar1)})) !== (undefined))) { + this.__backing_paramVar1!.update((initializers!.paramVar1 as Per)); + } + if (((({let gensym___68516859 = initializers; + (((gensym___68516859) == (null)) ? undefined : gensym___68516859.paramVar2)})) !== (undefined))) { + this.__backing_paramVar2!.update((initializers!.paramVar2 as Array)); + } + if (((({let gensym___96937083 = initializers; + (((gensym___96937083) == (null)) ? undefined : gensym___96937083.paramVar3)})) !== (undefined))) { + this.__backing_paramVar3!.update((initializers!.paramVar3 as StateType)); + } + if (((({let gensym___151087626 = initializers; + (((gensym___151087626) == (null)) ? undefined : gensym___151087626.paramVar4)})) !== (undefined))) { + this.__backing_paramVar4!.update((initializers!.paramVar4 as Set)); + } + if (((({let gensym___127163363 = initializers; + (((gensym___127163363) == (null)) ? undefined : gensym___127163363.paramVar5)})) !== (undefined))) { + this.__backing_paramVar5!.update((initializers!.paramVar5 as Array)); + } + if (((({let gensym___67758341 = initializers; + (((gensym___67758341) == (null)) ? undefined : gensym___67758341.paramVar6)})) !== (undefined))) { + this.__backing_paramVar6!.update((initializers!.paramVar6 as Array)); + } + if (((({let gensym___248313276 = initializers; + (((gensym___248313276) == (null)) ? undefined : gensym___248313276.paramVar7)})) !== (undefined))) { + this.__backing_paramVar7!.update((initializers!.paramVar7 as Array)); + } + if (((({let gensym___215432014 = initializers; + (((gensym___215432014) == (null)) ? undefined : gensym___215432014.paramVar8)})) !== (undefined))) { + this.__backing_paramVar8!.update((initializers!.paramVar8 as ((sr: string)=> void))); + } + if (((({let gensym___69570417 = initializers; + (((gensym___69570417) == (null)) ? undefined : gensym___69570417.paramVar9)})) !== (undefined))) { + this.__backing_paramVar9!.update((initializers!.paramVar9 as Date)); + } + if (((({let gensym___121882834 = initializers; + (((gensym___121882834) == (null)) ? undefined : gensym___121882834.paramVar10)})) !== (undefined))) { + this.__backing_paramVar10!.update((initializers!.paramVar10 as Map)); + } + if (((({let gensym___56641620 = initializers; + (((gensym___56641620) == (null)) ? undefined : gensym___56641620.paramVar11)})) !== (undefined))) { + this.__backing_paramVar11!.update((initializers!.paramVar11 as (string | number))); + } + if (((({let gensym___51325313 = initializers; + (((gensym___51325313) == (null)) ? undefined : gensym___51325313.paramVar12)})) !== (undefined))) { + this.__backing_paramVar12!.update((initializers!.paramVar12 as (Set | Per))); + } + } + + private __backing_paramVar1?: IParamDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + private __backing_paramVar2?: IParamDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + private __backing_paramVar3?: IParamDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + private __backing_paramVar4?: IParamDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + private __backing_paramVar5?: IParamDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + private __backing_paramVar6?: IParamDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + private __backing_paramVar7?: IParamDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + private __backing_paramVar8?: IParamDecoratedVariable<((sr: string)=> void)>; + + public get paramVar8(): ((sr: string)=> void) { + return this.__backing_paramVar8!.get(); + } + + private __backing_paramVar9?: IParamDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + private __backing_paramVar10?: IParamDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + private __backing_paramVar11?: IParamDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + private __backing_paramVar12?: IParamDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IParamDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IParamDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IParamDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IParamDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IParamDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IParamDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IParamDecoratedVariable> | undefined) + set paramVar8(paramVar8: (((sr: string)=> void) | undefined)) + + get paramVar8(): (((sr: string)=> void) | undefined) + set __backing_paramVar8(__backing_paramVar8: (IParamDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_paramVar8(): (IParamDecoratedVariable<((sr: string)=> void)> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IParamDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IParamDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IParamDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IParamDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IParamDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IParamDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IParamDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IParamDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Param decorated variables transformation', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ea06e80ed76fe1c6470f11a10fab775f53ffc63 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/param/param-with-require.test.ts @@ -0,0 +1,347 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const OBSERVED_DIR_PATH: string = 'decorators/param'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, OBSERVED_DIR_PATH, 'param-with-require.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @Param Decorator with @Require', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, ForEach as ForEach, Button as Button, Text as Text } from "@ohos.arkui.component"; + +import { Param as Param, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +class Region { + public x: number; + + public y: number; + + public constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + +} + +class Info { + public name: string; + + public age: number; + + public region: Region; + + public constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + @Local() public infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; + + public build() { + Column(){ + ForEach(this.infoList, ((info: Info) => { + MiddleComponent({ + info: info, + }); + })); + Button("change").onClick(((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + })); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct MiddleComponent extends CustomComponentV2 { + @Require() @Param() public info!: Info; + + public build() { + Column(){ + Text(\`name: \${this.info.name}\`); + Text(\`age: \${this.info.age}\`); + SubComponent({ + region: this.info.region, + }); + }; + } + + public constructor() {} + +} + +@ComponentV2() final struct SubComponent extends CustomComponentV2 { + @Require() @Param() public region!: Region; + + public build() { + Column(){ + Text(\`region: \${this.region.x}-\${this.region.y}\`); + }; + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + infoList?: Info[]; + @Local() __backing_infoList?: Info[]; + +} + +@ComponentV2() export interface __Options_MiddleComponent { + info?: Info; + @Require() @Param() __backing_info?: Info; + +} + +@ComponentV2() export interface __Options_SubComponent { + region?: Region; + @Require() @Param() __backing_region?: Region; + +} +`; + +const expectedCheckedScript: string = ` +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { ILocalDecoratedVariable as ILocalDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, Column as Column, ForEach as ForEach, Button as Button, Text as Text } from "@ohos.arkui.component"; + +import { Param as Param, Require as Require, Local as Local } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Region { + public x: number; + + public y: number; + + public constructor(x: number, y: number) { + this.x = x; + this.y = y; + } + +} + +class Info { + public name: string; + + public age: number; + + public region: Region; + + public constructor(name: string, age: number, x: number, y: number) { + this.name = name; + this.age = age; + this.region = new Region(x, y); + } + +} + +@ComponentV2() final struct Index extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_infoList = STATE_MGMT_FACTORY.makeLocal>(this, "infoList", [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]); + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_infoList?: ILocalDecoratedVariable>; + + public get infoList(): Array { + return this.__backing_infoList!.get(); + } + + public set infoList(value: Array) { + this.__backing_infoList!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.infoList; + }), ((info: Info) => { + MiddleComponent._instantiateImpl(undefined, (() => { + return new MiddleComponent(); + }), { + info: info, + }, undefined, undefined); + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.infoList[0] = new Info("Atom", 40, 27, 90); + this.infoList[1].name = "Bob"; + this.infoList[2].region = new Region(7, 9); + })); + return; + }), "change", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct MiddleComponent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_MiddleComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_info = STATE_MGMT_FACTORY.makeParam(this, "info", (initializers!.info as Info)); + } + + public __updateStruct(initializers: (__Options_MiddleComponent | undefined)): void { + if (((({let gensym___152615498 = initializers; + (((gensym___152615498) == (null)) ? undefined : gensym___152615498.info)})) !== (undefined))) { + this.__backing_info!.update((initializers!.info as Info)); + } + } + + private __backing_info?: IParamDecoratedVariable; + + public get info(): Info { + return this.__backing_info!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`name: \${this.info.name}\`, undefined, undefined); + Text(undefined, \`age: \${this.info.age}\`, undefined, undefined); + SubComponent._instantiateImpl(undefined, (() => { + return new SubComponent(); + }), { + region: this.info.region, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct SubComponent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_SubComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_region = STATE_MGMT_FACTORY.makeParam(this, "region", (initializers!.region as Region)); + } + + public __updateStruct(initializers: (__Options_SubComponent | undefined)): void { + if (((({let gensym___240509478 = initializers; + (((gensym___240509478) == (null)) ? undefined : gensym___240509478.region)})) !== (undefined))) { + this.__backing_region!.update((initializers!.region as Region)); + } + } + + private __backing_region?: IParamDecoratedVariable; + + public get region(): Region { + return this.__backing_region!.get(); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`region: \${this.region.x}-\${this.region.y}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Index { + set infoList(infoList: (Array | undefined)) + + get infoList(): (Array | undefined) + set __backing_infoList(__backing_infoList: (ILocalDecoratedVariable> | undefined)) + + get __backing_infoList(): (ILocalDecoratedVariable> | undefined) + +} + +@ComponentV2() export interface __Options_MiddleComponent { + set info(info: (Info | undefined)) + + get info(): (Info | undefined) + @Require() set __backing_info(__backing_info: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_info(): (IParamDecoratedVariable | undefined) + +} + +@ComponentV2() export interface __Options_SubComponent { + set region(region: (Region | undefined)) + + get region(): (Region | undefined) + @Require() set __backing_region(__backing_region: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_region(): (IParamDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Param Decorator with @Require', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3b5e8f98bcd314703632ed433700712b5fea6d3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-basic-type.test.ts @@ -0,0 +1,198 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @PropRef decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-ref-basic-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? ("propVar1"))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as string)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as number)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as boolean)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as undefined)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as null)); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable; + + public get propVar2(): number { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: number) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable; + + public get propVar4(): undefined { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: undefined) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: (number | undefined)) + + get propVar2(): (number | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @PropRef decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..8dbc549f546a17c785299f5429fe65e05148ae7f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-complex-type.test.ts @@ -0,0 +1,427 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @PropRef decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed-trans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class PropType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: PropType = new PropType(0, 0); + + public static readonly TYPE2: PropType = new PropType(1, 1); + + public static readonly TYPE3: PropType = new PropType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + + public getName(): String { + return PropType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): PropType { + for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { + if (((name) == (PropType.#NamesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant PropType.") + (name))); + } + + public static fromValue(value: int): PropType { + for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { + if (((value) == (PropType.#ValuesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum PropType with value ") + (value))); + } + + public valueOf(): int { + return PropType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return PropType.#StringValuesArray[this.#ordinal]; + } + + public static values(): PropType[] { + return PropType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: PropType): String { + return e.getName(); + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); + this.__backing_propVar6 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar6", ((({let gensym___128760941 = initializers; + (((gensym___128760941) == (null)) ? undefined : gensym___128760941.propVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_propVar7 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar7", ((({let gensym___30534085 = initializers; + (((gensym___30534085) == (null)) ? undefined : gensym___30534085.propVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_propVar8 = STATE_MGMT_FACTORY.makePropRef<((sr: string)=> void)>(this, "propVar8", ((({let gensym___12471776 = initializers; + (((gensym___12471776) == (null)) ? undefined : gensym___12471776.propVar8)})) ?? (((sr: string) => {})))); + this.__backing_propVar9 = STATE_MGMT_FACTORY.makePropRef(this, "propVar9", ((({let gensym___123472108 = initializers; + (((gensym___123472108) == (null)) ? undefined : gensym___123472108.propVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_propVar10 = STATE_MGMT_FACTORY.makePropRef>(this, "propVar10", ((({let gensym___147847012 = initializers; + (((gensym___147847012) == (null)) ? undefined : gensym___147847012.propVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_propVar11 = STATE_MGMT_FACTORY.makePropRef<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; + (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); + this.__backing_propVar12 = STATE_MGMT_FACTORY.makePropRef<(Set | Per)>(this, "propVar12", ((({let gensym___220245132 = initializers; + (((gensym___220245132) == (null)) ? undefined : gensym___220245132.propVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as Per)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as Array)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as PropType)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as Set)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as Array)); + } + if (((({let gensym___211600890 = initializers; + (((gensym___211600890) == (null)) ? undefined : gensym___211600890.propVar6)})) !== (undefined))) { + this.__backing_propVar6!.update((initializers!.propVar6 as Array)); + } + if (((({let gensym___124229427 = initializers; + (((gensym___124229427) == (null)) ? undefined : gensym___124229427.propVar7)})) !== (undefined))) { + this.__backing_propVar7!.update((initializers!.propVar7 as Array)); + } + if (((({let gensym___248056380 = initializers; + (((gensym___248056380) == (null)) ? undefined : gensym___248056380.propVar8)})) !== (undefined))) { + this.__backing_propVar8!.update((initializers!.propVar8 as ((sr: string)=> void))); + } + if (((({let gensym___55399278 = initializers; + (((gensym___55399278) == (null)) ? undefined : gensym___55399278.propVar9)})) !== (undefined))) { + this.__backing_propVar9!.update((initializers!.propVar9 as Date)); + } + if (((({let gensym___125042885 = initializers; + (((gensym___125042885) == (null)) ? undefined : gensym___125042885.propVar10)})) !== (undefined))) { + this.__backing_propVar10!.update((initializers!.propVar10 as Map)); + } + if (((({let gensym___2015283 = initializers; + (((gensym___2015283) == (null)) ? undefined : gensym___2015283.propVar11)})) !== (undefined))) { + this.__backing_propVar11!.update((initializers!.propVar11 as (string | number))); + } + if (((({let gensym___39009414 = initializers; + (((gensym___39009414) == (null)) ? undefined : gensym___39009414.propVar12)})) !== (undefined))) { + this.__backing_propVar12!.update((initializers!.propVar12 as (Set | Per))); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): Per { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: Per) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable>; + + public get propVar2(): Array { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: Array) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): PropType { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: PropType) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable>; + + public get propVar4(): Set { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: Set) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable>; + + public get propVar5(): Array { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: Array) { + this.__backing_propVar5!.set(value); + } + + private __backing_propVar6?: IPropRefDecoratedVariable>; + + public get propVar6(): Array { + return this.__backing_propVar6!.get(); + } + + public set propVar6(value: Array) { + this.__backing_propVar6!.set(value); + } + + private __backing_propVar7?: IPropRefDecoratedVariable>; + + public get propVar7(): Array { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: Array) { + this.__backing_propVar7!.set(value); + } + + private __backing_propVar8?: IPropRefDecoratedVariable<((sr: string)=> void)>; + + public get propVar8(): ((sr: string)=> void) { + return this.__backing_propVar8!.get(); + } + + public set propVar8(value: ((sr: string)=> void)) { + this.__backing_propVar8!.set(value); + } + + private __backing_propVar9?: IPropRefDecoratedVariable; + + public get propVar9(): Date { + return this.__backing_propVar9!.get(); + } + + public set propVar9(value: Date) { + this.__backing_propVar9!.set(value); + } + + private __backing_propVar10?: IPropRefDecoratedVariable>; + + public get propVar10(): Map { + return this.__backing_propVar10!.get(); + } + + public set propVar10(value: Map) { + this.__backing_propVar10!.set(value); + } + + private __backing_propVar11?: IPropRefDecoratedVariable<(string | number)>; + + public get propVar11(): (string | number) { + return this.__backing_propVar11!.get(); + } + + public set propVar11(value: (string | number)) { + this.__backing_propVar11!.set(value); + } + + private __backing_propVar12?: IPropRefDecoratedVariable<(Set | Per)>; + + public get propVar12(): (Set | Per) { + return this.__backing_propVar12!.get(); + } + + public set propVar12(value: (Set | Per)) { + this.__backing_propVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set propVar1(propVar1: (Per | undefined)) + + get propVar1(): (Per | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: (Array | undefined)) + + get propVar2(): (Array | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable> | undefined) + set propVar3(propVar3: (PropType | undefined)) + + get propVar3(): (PropType | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (Set | undefined)) + + get propVar4(): (Set | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable> | undefined) + set propVar5(propVar5: (Array | undefined)) + + get propVar5(): (Array | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable> | undefined) + set propVar6(propVar6: (Array | undefined)) + + get propVar6(): (Array | undefined) + set __backing_propVar6(__backing_propVar6: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar6(): (IPropRefDecoratedVariable> | undefined) + set propVar7(propVar7: (Array | undefined)) + + get propVar7(): (Array | undefined) + set __backing_propVar7(__backing_propVar7: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar7(): (IPropRefDecoratedVariable> | undefined) + set propVar8(propVar8: (((sr: string)=> void) | undefined)) + + get propVar8(): (((sr: string)=> void) | undefined) + set __backing_propVar8(__backing_propVar8: (IPropRefDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_propVar8(): (IPropRefDecoratedVariable<((sr: string)=> void)> | undefined) + set propVar9(propVar9: (Date | undefined)) + + get propVar9(): (Date | undefined) + set __backing_propVar9(__backing_propVar9: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar9(): (IPropRefDecoratedVariable | undefined) + set propVar10(propVar10: (Map | undefined)) + + get propVar10(): (Map | undefined) + set __backing_propVar10(__backing_propVar10: (IPropRefDecoratedVariable> | undefined)) + + get __backing_propVar10(): (IPropRefDecoratedVariable> | undefined) + set propVar11(propVar11: ((string | number) | undefined)) + + get propVar11(): ((string | number) | undefined) + set __backing_propVar11(__backing_propVar11: (IPropRefDecoratedVariable<(string | number)> | undefined)) + + get __backing_propVar11(): (IPropRefDecoratedVariable<(string | number)> | undefined) + set propVar12(propVar12: ((Set | Per) | undefined)) + + get propVar12(): ((Set | Per) | undefined) + set __backing_propVar12(__backing_propVar12: (IPropRefDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_propVar12(): (IPropRefDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @PropRef decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e176c96e9ec5f2b52d23b06dff88706fb6e3659a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/prop-ref-without-initialization.test.ts @@ -0,0 +1,285 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-ref-without-initialization.ets'), +]; + +const pluginTester = new PluginTester('test @PropRef decorated variables transformation without initialization', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed-trans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +@Component() final struct PropParent extends CustomComponent { + @PropRef() public propVar1!: string; + + @PropRef() public propVar2?: (number | undefined); + + @PropRef() public propVar3!: boolean; + + @PropRef() public propVar4?: undefined; + + @PropRef() public propVar5?: null; + + @PropRef() public propVar6?: (Array | null); + + @PropRef() public propVar7?: (Map | undefined); + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + propVar1?: string; + @PropRef() __backing_propVar1?: string; + propVar2?: (number | undefined); + @PropRef() __backing_propVar2?: (number | undefined); + propVar3?: boolean; + @PropRef() __backing_propVar3?: boolean; + propVar4?: undefined; + @PropRef() __backing_propVar4?: undefined; + propVar5?: null; + @PropRef() __backing_propVar5?: null; + propVar6?: (Array | null); + @PropRef() __backing_propVar6?: (Array | null); + propVar7?: (Map | undefined); + @PropRef() __backing_propVar7?: (Map | undefined); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { PropRef as PropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makePropRef(this, "propVar1", (initializers!.propVar1 as string)); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makePropRef<(number | undefined)>(this, "propVar2", (initializers!.propVar2 as (number | undefined))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makePropRef(this, "propVar3", (initializers!.propVar3 as boolean)); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makePropRef(this, "propVar4", (initializers!.propVar4 as undefined)); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makePropRef(this, "propVar5", (initializers!.propVar5 as null)); + this.__backing_propVar6 = STATE_MGMT_FACTORY.makePropRef<(Array | null)>(this, "propVar6", (initializers!.propVar6 as (Array | null))); + this.__backing_propVar7 = STATE_MGMT_FACTORY.makePropRef<(Map | undefined)>(this, "propVar7", (initializers!.propVar7 as (Map | undefined))); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as string)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as (number | undefined))); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as boolean)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as undefined)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as null)); + } + if (((({let gensym___211600890 = initializers; + (((gensym___211600890) == (null)) ? undefined : gensym___211600890.propVar6)})) !== (undefined))) { + this.__backing_propVar6!.update((initializers!.propVar6 as (Array | null))); + } + if (((({let gensym___124229427 = initializers; + (((gensym___124229427) == (null)) ? undefined : gensym___124229427.propVar7)})) !== (undefined))) { + this.__backing_propVar7!.update((initializers!.propVar7 as (Map | undefined))); + } + } + + private __backing_propVar1?: IPropRefDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropRefDecoratedVariable<(number | undefined)>; + + public get propVar2(): (number | undefined) { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: (number | undefined)) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropRefDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropRefDecoratedVariable; + + public get propVar4(): undefined { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: undefined) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropRefDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + private __backing_propVar6?: IPropRefDecoratedVariable<(Array | null)>; + + public get propVar6(): (Array | null) { + return this.__backing_propVar6!.get(); + } + + public set propVar6(value: (Array | null)) { + this.__backing_propVar6!.set(value); + } + + private __backing_propVar7?: IPropRefDecoratedVariable<(Map | undefined)>; + + public get propVar7(): (Map | undefined) { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: (Map | undefined)) { + this.__backing_propVar7!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropRefDecoratedVariable | undefined) + set propVar2(propVar2: ((number | undefined) | undefined)) + + get propVar2(): ((number | undefined) | undefined) + set __backing_propVar2(__backing_propVar2: (IPropRefDecoratedVariable<(number | undefined)> | undefined)) + + get __backing_propVar2(): (IPropRefDecoratedVariable<(number | undefined)> | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropRefDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropRefDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropRefDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropRefDecoratedVariable | undefined) + set propVar6(propVar6: ((Array | null) | undefined)) + + get propVar6(): ((Array | null) | undefined) + set __backing_propVar6(__backing_propVar6: (IPropRefDecoratedVariable<(Array | null)> | undefined)) + + get __backing_propVar6(): (IPropRefDecoratedVariable<(Array | null)> | undefined) + set propVar7(propVar7: ((Map | undefined) | undefined)) + + get propVar7(): ((Map | undefined) | undefined) + set __backing_propVar7(__backing_propVar7: (IPropRefDecoratedVariable<(Map | undefined)> | undefined)) + + get __backing_propVar7(): (IPropRefDecoratedVariable<(Map | undefined)> | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @PropRef decorated variables transformation without initialization', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..93799cce2050e13aa62eec27a6fd10faaa0bed59 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop-ref/state-to-propref.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-prop-ref.ets'), +]; + +const pluginTester = new PluginTester('test @PropRef decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; + +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, Button as Button, Column as Column, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { PropRef as PropRef, State as State } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct CountDownComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_CountDownComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makePropRef(this, "count", ((({let gensym___58710805 = initializers; + (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? (0))); + this.__backing_costOfOneAttempt = ((({let gensym___88948111 = initializers; + (((gensym___88948111) == (null)) ? undefined : gensym___88948111.costOfOneAttempt)})) ?? (1)); + } + + public __updateStruct(initializers: (__Options_CountDownComponent | undefined)): void { + if (((({let gensym___188547633 = initializers; + (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { + this.__backing_count!.update((initializers!.count as number)); + } + } + + private __backing_count?: IPropRefDecoratedVariable; + + public get count(): number { + return this.__backing_count!.get(); + } + + public set count(value: number) { + this.__backing_count!.set(value); + } + + private __backing_costOfOneAttempt?: number; + + public get costOfOneAttempt(): number { + return (this.__backing_costOfOneAttempt as number); + } + + public set costOfOneAttempt(value: number) { + this.__backing_costOfOneAttempt = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (((this.count) > (0))) { + ConditionBranch(@memo() (() => { + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, "Game over!", undefined, undefined); + })); + } + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + })); + return; + }), "Try again", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() final struct ParentComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_countDownStartValue = STATE_MGMT_FACTORY.makeState(this, "countDownStartValue", ((({let gensym___249912438 = initializers; + (((gensym___249912438) == (null)) ? undefined : gensym___249912438.countDownStartValue)})) ?? (10))); + } + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_countDownStartValue?: IStateDecoratedVariable; + + public get countDownStartValue(): number { + return this.__backing_countDownStartValue!.get(); + } + + public set countDownStartValue(value: number) { + this.__backing_countDownStartValue!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.countDownStartValue += 1; + })); + return; + }), "+1 - Nuggets in New Game", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.countDownStartValue -= 1; + })); + return; + }), "-1 - Nuggets in New Game", undefined, undefined); + CountDownComponent._instantiateImpl(undefined, (() => { + return new CountDownComponent(); + }), { + count: this.countDownStartValue, + costOfOneAttempt: 2, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_CountDownComponent { + set count(count: (number | undefined)) + + get count(): (number | undefined) + set __backing_count(__backing_count: (IPropRefDecoratedVariable | undefined)) + + get __backing_count(): (IPropRefDecoratedVariable | undefined) + set costOfOneAttempt(costOfOneAttempt: (number | undefined)) + + get costOfOneAttempt(): (number | undefined) + +} + +@Component() export interface __Options_ParentComponent { + set countDownStartValue(countDownStartValue: (number | undefined)) + + get countDownStartValue(): (number | undefined) + set __backing_countDownStartValue(__backing_countDownStartValue: (IStateDecoratedVariable | undefined)) + + get __backing_countDownStartValue(): (IStateDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @PropRef decorated variables passing', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts index 9ed1637237c1bb9de9903aebfa230cc77d88d070..9b58c22be8260da0b7ccc323b62f7aabeb65aee7 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,30 +38,38 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class PropParent extends CustomComponent { - public __initializeStruct(initializers: __Options_PropParent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_propVar1 = new PropDecoratedVariable("propVar1", ((({let gensym___95172135 = initializers; + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? ("propVar1"))); - this.__backing_propVar2 = new PropDecoratedVariable("propVar2", ((({let gensym___222490386 = initializers; + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp(this, "propVar2", ((({let gensym___222490386 = initializers; (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); - this.__backing_propVar3 = new PropDecoratedVariable("propVar3", ((({let gensym___201781257 = initializers; + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); - this.__backing_propVar4 = new PropDecoratedVariable("propVar4", ((({let gensym___22028950 = initializers; + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp(this, "propVar4", ((({let gensym___22028950 = initializers; (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); - this.__backing_propVar5 = new PropDecoratedVariable("propVar5", ((({let gensym___54872258 = initializers; + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp(this, "propVar5", ((({let gensym___54872258 = initializers; (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? (null))); } - public __updateStruct(initializers: __Options_PropParent | undefined): void { + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { if (((({let gensym___67969738 = initializers; (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { this.__backing_propVar1!.update((initializers!.propVar1 as string)); @@ -82,66 +91,95 @@ function main() {} this.__backing_propVar5!.update((initializers!.propVar5 as null)); } } - private __backing_propVar1?: PropDecoratedVariable; + + private __backing_propVar1?: IPropDecoratedVariable; + public get propVar1(): string { return this.__backing_propVar1!.get(); } + public set propVar1(value: string) { this.__backing_propVar1!.set(value); } - private __backing_propVar2?: PropDecoratedVariable; + + private __backing_propVar2?: IPropDecoratedVariable; + public get propVar2(): number { return this.__backing_propVar2!.get(); } + public set propVar2(value: number) { this.__backing_propVar2!.set(value); } - private __backing_propVar3?: PropDecoratedVariable; + + private __backing_propVar3?: IPropDecoratedVariable; + public get propVar3(): boolean { return this.__backing_propVar3!.get(); } + public set propVar3(value: boolean) { this.__backing_propVar3!.set(value); } - private __backing_propVar4?: PropDecoratedVariable; + + private __backing_propVar4?: IPropDecoratedVariable; + public get propVar4(): undefined { return this.__backing_propVar4!.get(); } + public set propVar4(value: undefined) { this.__backing_propVar4!.set(value); } - private __backing_propVar5?: PropDecoratedVariable; + + private __backing_propVar5?: IPropDecoratedVariable; + public get propVar5(): null { return this.__backing_propVar5!.get(); } + public set propVar5(value: null) { this.__backing_propVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: PropParent)=> PropParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_PropParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_PropParent { - set propVar1(propVar1: string | undefined) - get propVar1(): string | undefined - set __backing_propVar1(__backing_propVar1: PropDecoratedVariable | undefined) - get __backing_propVar1(): PropDecoratedVariable | undefined - set propVar2(propVar2: number | undefined) - get propVar2(): number | undefined - set __backing_propVar2(__backing_propVar2: PropDecoratedVariable | undefined) - get __backing_propVar2(): PropDecoratedVariable | undefined - set propVar3(propVar3: boolean | undefined) - get propVar3(): boolean | undefined - set __backing_propVar3(__backing_propVar3: PropDecoratedVariable | undefined) - get __backing_propVar3(): PropDecoratedVariable | undefined - set propVar4(propVar4: undefined | undefined) - get propVar4(): undefined | undefined - set __backing_propVar4(__backing_propVar4: PropDecoratedVariable | undefined) - get __backing_propVar4(): PropDecoratedVariable | undefined - set propVar5(propVar5: null | undefined) - get propVar5(): null | undefined - set __backing_propVar5(__backing_propVar5: PropDecoratedVariable | undefined) - get __backing_propVar5(): PropDecoratedVariable | undefined +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (number | undefined)) + + get propVar2(): (number | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable | undefined) + } `; @@ -151,9 +189,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Prop decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts index 7db4457811022389e20f8b2b1ab1b2d0067444d1..c619e10ce290e731a8a5a2db4a4ec90274217eac 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class PropType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: PropType = new PropType(0, 0); + public static readonly TYPE2: PropType = new PropType(1, 1); + public static readonly TYPE3: PropType = new PropType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + public getName(): String { return PropType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): PropType { for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { if (((name) == (PropType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class PropType extends BaseEnum { } throw new Error((("No enum constant PropType.") + (name))); } + public static fromValue(value: int): PropType { for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { if (((value) == (PropType.#ValuesArray[i]))) { @@ -87,51 +107,58 @@ final class PropType extends BaseEnum { } throw new Error((("No enum PropType with value ") + (value))); } + public valueOf(): int { return PropType.#ValuesArray[this.#ordinal]; } + public toString(): String { return PropType.#StringValuesArray[this.#ordinal]; } + public static values(): PropType[] { return PropType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: PropType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_propVar1 = new PropDecoratedVariable("propVar1", ((({let gensym___95172135 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); - this.__backing_propVar2 = new PropDecoratedVariable>("propVar2", ((({let gensym___222490386 = initializers; + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp>(this, "propVar2", ((({let gensym___222490386 = initializers; (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (new Array(3, 6, 8)))); - this.__backing_propVar3 = new PropDecoratedVariable("propVar3", ((({let gensym___201781257 = initializers; + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); - this.__backing_propVar4 = new PropDecoratedVariable>("propVar4", ((({let gensym___22028950 = initializers; + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp>(this, "propVar4", ((({let gensym___22028950 = initializers; (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (new Set(new Array("aa", "bb"))))); - this.__backing_propVar5 = new PropDecoratedVariable>("propVar5", ((({let gensym___54872258 = initializers; + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp>(this, "propVar5", ((({let gensym___54872258 = initializers; (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); - this.__backing_propVar6 = new PropDecoratedVariable>("propVar6", ((({let gensym___128760941 = initializers; + this.__backing_propVar6 = STATE_MGMT_FACTORY.makeProp>(this, "propVar6", ((({let gensym___128760941 = initializers; (((gensym___128760941) == (null)) ? undefined : gensym___128760941.propVar6)})) ?? (new Array(new Per(7), new Per(11))))); - this.__backing_propVar7 = new PropDecoratedVariable>("propVar7", ((({let gensym___30534085 = initializers; + this.__backing_propVar7 = STATE_MGMT_FACTORY.makeProp>(this, "propVar7", ((({let gensym___30534085 = initializers; (((gensym___30534085) == (null)) ? undefined : gensym___30534085.propVar7)})) ?? ([new Per(7), new Per(11)]))); - this.__backing_propVar8 = new PropDecoratedVariable<((sr: string)=> void)>("propVar8", ((({let gensym___12471776 = initializers; + this.__backing_propVar8 = STATE_MGMT_FACTORY.makeProp<((sr: string)=> void)>(this, "propVar8", ((({let gensym___12471776 = initializers; (((gensym___12471776) == (null)) ? undefined : gensym___12471776.propVar8)})) ?? (((sr: string) => {})))); - this.__backing_propVar9 = new PropDecoratedVariable("propVar9", ((({let gensym___123472108 = initializers; + this.__backing_propVar9 = STATE_MGMT_FACTORY.makeProp(this, "propVar9", ((({let gensym___123472108 = initializers; (((gensym___123472108) == (null)) ? undefined : gensym___123472108.propVar9)})) ?? (new Date("2025-4-23")))); - this.__backing_propVar10 = new PropDecoratedVariable>("propVar10", ((({let gensym___147847012 = initializers; + this.__backing_propVar10 = STATE_MGMT_FACTORY.makeProp>(this, "propVar10", ((({let gensym___147847012 = initializers; (((gensym___147847012) == (null)) ? undefined : gensym___147847012.propVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); - this.__backing_propVar11 = new PropDecoratedVariable("propVar11", ((({let gensym___117026760 = initializers; + this.__backing_propVar11 = STATE_MGMT_FACTORY.makeProp<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); - this.__backing_propVar12 = new PropDecoratedVariable | Per>("propVar12", ((({let gensym___220245132 = initializers; + this.__backing_propVar12 = STATE_MGMT_FACTORY.makeProp<(Set | Per)>(this, "propVar12", ((({let gensym___220245132 = initializers; (((gensym___220245132) == (null)) ? undefined : gensym___220245132.propVar12)})) ?? (new Per(6)))); } - public __updateStruct(initializers: __Options_Parent | undefined): void { + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { if (((({let gensym___67969738 = initializers; (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { this.__backing_propVar1!.update((initializers!.propVar1 as Per)); @@ -174,150 +201,214 @@ final class PropType extends BaseEnum { } if (((({let gensym___2015283 = initializers; (((gensym___2015283) == (null)) ? undefined : gensym___2015283.propVar11)})) !== (undefined))) { - this.__backing_propVar11!.update((initializers!.propVar11 as string | number)); + this.__backing_propVar11!.update((initializers!.propVar11 as (string | number))); } if (((({let gensym___39009414 = initializers; (((gensym___39009414) == (null)) ? undefined : gensym___39009414.propVar12)})) !== (undefined))) { - this.__backing_propVar12!.update((initializers!.propVar12 as Set | Per)); + this.__backing_propVar12!.update((initializers!.propVar12 as (Set | Per))); } } - private __backing_propVar1?: PropDecoratedVariable; + + private __backing_propVar1?: IPropDecoratedVariable; + public get propVar1(): Per { return this.__backing_propVar1!.get(); } + public set propVar1(value: Per) { this.__backing_propVar1!.set(value); } - private __backing_propVar2?: PropDecoratedVariable>; + + private __backing_propVar2?: IPropDecoratedVariable>; + public get propVar2(): Array { return this.__backing_propVar2!.get(); } + public set propVar2(value: Array) { this.__backing_propVar2!.set(value); } - private __backing_propVar3?: PropDecoratedVariable; + + private __backing_propVar3?: IPropDecoratedVariable; + public get propVar3(): PropType { return this.__backing_propVar3!.get(); } + public set propVar3(value: PropType) { this.__backing_propVar3!.set(value); } - private __backing_propVar4?: PropDecoratedVariable>; + + private __backing_propVar4?: IPropDecoratedVariable>; + public get propVar4(): Set { return this.__backing_propVar4!.get(); } + public set propVar4(value: Set) { this.__backing_propVar4!.set(value); } - private __backing_propVar5?: PropDecoratedVariable>; + + private __backing_propVar5?: IPropDecoratedVariable>; + public get propVar5(): Array { return this.__backing_propVar5!.get(); } + public set propVar5(value: Array) { this.__backing_propVar5!.set(value); } - private __backing_propVar6?: PropDecoratedVariable>; + + private __backing_propVar6?: IPropDecoratedVariable>; + public get propVar6(): Array { return this.__backing_propVar6!.get(); } + public set propVar6(value: Array) { this.__backing_propVar6!.set(value); } - private __backing_propVar7?: PropDecoratedVariable>; + + private __backing_propVar7?: IPropDecoratedVariable>; + public get propVar7(): Array { return this.__backing_propVar7!.get(); } + public set propVar7(value: Array) { this.__backing_propVar7!.set(value); } - private __backing_propVar8?: PropDecoratedVariable<((sr: string)=> void)>; + + private __backing_propVar8?: IPropDecoratedVariable<((sr: string)=> void)>; + public get propVar8(): ((sr: string)=> void) { return this.__backing_propVar8!.get(); } + public set propVar8(value: ((sr: string)=> void)) { this.__backing_propVar8!.set(value); } - private __backing_propVar9?: PropDecoratedVariable; + + private __backing_propVar9?: IPropDecoratedVariable; + public get propVar9(): Date { return this.__backing_propVar9!.get(); } + public set propVar9(value: Date) { this.__backing_propVar9!.set(value); } - private __backing_propVar10?: PropDecoratedVariable>; + + private __backing_propVar10?: IPropDecoratedVariable>; + public get propVar10(): Map { return this.__backing_propVar10!.get(); } + public set propVar10(value: Map) { this.__backing_propVar10!.set(value); } - private __backing_propVar11?: PropDecoratedVariable; - public get propVar11(): string | number { + + private __backing_propVar11?: IPropDecoratedVariable<(string | number)>; + + public get propVar11(): (string | number) { return this.__backing_propVar11!.get(); } - public set propVar11(value: string | number) { + + public set propVar11(value: (string | number)) { this.__backing_propVar11!.set(value); } - private __backing_propVar12?: PropDecoratedVariable | Per>; - public get propVar12(): Set | Per { + + private __backing_propVar12?: IPropDecoratedVariable<(Set | Per)>; + + public get propVar12(): (Set | Per) { return this.__backing_propVar12!.get(); } - public set propVar12(value: Set | Per) { + + public set propVar12(value: (Set | Per)) { this.__backing_propVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set propVar1(propVar1: Per | undefined) - get propVar1(): Per | undefined - set __backing_propVar1(__backing_propVar1: PropDecoratedVariable | undefined) - get __backing_propVar1(): PropDecoratedVariable | undefined - set propVar2(propVar2: Array | undefined) - get propVar2(): Array | undefined - set __backing_propVar2(__backing_propVar2: PropDecoratedVariable> | undefined) - get __backing_propVar2(): PropDecoratedVariable> | undefined - set propVar3(propVar3: PropType | undefined) - get propVar3(): PropType | undefined - set __backing_propVar3(__backing_propVar3: PropDecoratedVariable | undefined) - get __backing_propVar3(): PropDecoratedVariable | undefined - set propVar4(propVar4: Set | undefined) - get propVar4(): Set | undefined - set __backing_propVar4(__backing_propVar4: PropDecoratedVariable> | undefined) - get __backing_propVar4(): PropDecoratedVariable> | undefined - set propVar5(propVar5: Array | undefined) - get propVar5(): Array | undefined - set __backing_propVar5(__backing_propVar5: PropDecoratedVariable> | undefined) - get __backing_propVar5(): PropDecoratedVariable> | undefined - set propVar6(propVar6: Array | undefined) - get propVar6(): Array | undefined - set __backing_propVar6(__backing_propVar6: PropDecoratedVariable> | undefined) - get __backing_propVar6(): PropDecoratedVariable> | undefined - set propVar7(propVar7: Array | undefined) - get propVar7(): Array | undefined - set __backing_propVar7(__backing_propVar7: PropDecoratedVariable> | undefined) - get __backing_propVar7(): PropDecoratedVariable> | undefined - set propVar8(propVar8: ((sr: string)=> void) | undefined) - get propVar8(): ((sr: string)=> void) | undefined - set __backing_propVar8(__backing_propVar8: PropDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_propVar8(): PropDecoratedVariable<((sr: string)=> void)> | undefined - set propVar9(propVar9: Date | undefined) - get propVar9(): Date | undefined - set __backing_propVar9(__backing_propVar9: PropDecoratedVariable | undefined) - get __backing_propVar9(): PropDecoratedVariable | undefined - set propVar10(propVar10: Map | undefined) - get propVar10(): Map | undefined - set __backing_propVar10(__backing_propVar10: PropDecoratedVariable> | undefined) - get __backing_propVar10(): PropDecoratedVariable> | undefined - set propVar11(propVar11: string | number | undefined) - get propVar11(): string | number | undefined - set __backing_propVar11(__backing_propVar11: PropDecoratedVariable | undefined) - get __backing_propVar11(): PropDecoratedVariable | undefined - set propVar12(propVar12: Set | Per | undefined) - get propVar12(): Set | Per | undefined - set __backing_propVar12(__backing_propVar12: PropDecoratedVariable | Per> | undefined) - get __backing_propVar12(): PropDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set propVar1(propVar1: (Per | undefined)) + + get propVar1(): (Per | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (Array | undefined)) + + get propVar2(): (Array | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable> | undefined) + set propVar3(propVar3: (PropType | undefined)) + + get propVar3(): (PropType | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (Set | undefined)) + + get propVar4(): (Set | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable> | undefined) + set propVar5(propVar5: (Array | undefined)) + + get propVar5(): (Array | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable> | undefined) + set propVar6(propVar6: (Array | undefined)) + + get propVar6(): (Array | undefined) + set __backing_propVar6(__backing_propVar6: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar6(): (IPropDecoratedVariable> | undefined) + set propVar7(propVar7: (Array | undefined)) + + get propVar7(): (Array | undefined) + set __backing_propVar7(__backing_propVar7: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar7(): (IPropDecoratedVariable> | undefined) + set propVar8(propVar8: (((sr: string)=> void) | undefined)) + + get propVar8(): (((sr: string)=> void) | undefined) + set __backing_propVar8(__backing_propVar8: (IPropDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_propVar8(): (IPropDecoratedVariable<((sr: string)=> void)> | undefined) + set propVar9(propVar9: (Date | undefined)) + + get propVar9(): (Date | undefined) + set __backing_propVar9(__backing_propVar9: (IPropDecoratedVariable | undefined)) + + get __backing_propVar9(): (IPropDecoratedVariable | undefined) + set propVar10(propVar10: (Map | undefined)) + + get propVar10(): (Map | undefined) + set __backing_propVar10(__backing_propVar10: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar10(): (IPropDecoratedVariable> | undefined) + set propVar11(propVar11: ((string | number) | undefined)) + + get propVar11(): ((string | number) | undefined) + set __backing_propVar11(__backing_propVar11: (IPropDecoratedVariable<(string | number)> | undefined)) + + get __backing_propVar11(): (IPropDecoratedVariable<(string | number)> | undefined) + set propVar12(propVar12: ((Set | Per) | undefined)) + + get propVar12(): ((Set | Per) | undefined) + set __backing_propVar12(__backing_propVar12: (IPropDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_propVar12(): (IPropDecoratedVariable<(Set | Per)> | undefined) + } `; @@ -327,9 +418,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Prop decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts index 0809c2d4618d31b1122ddb9aa96913a92229c623..3166366c13b4deff4ce9e99030039c5e5fca1d86 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,56 +38,77 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; + +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Text as Text, Button as Button, Column as Column, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class CountDownComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_CountDownComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_count = new PropDecoratedVariable("count", ((({let gensym___58710805 = initializers; +@Component() final struct CountDownComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_CountDownComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makeProp(this, "count", ((({let gensym___58710805 = initializers; (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? (0))); this.__backing_costOfOneAttempt = ((({let gensym___88948111 = initializers; (((gensym___88948111) == (null)) ? undefined : gensym___88948111.costOfOneAttempt)})) ?? (1)); } - public __updateStruct(initializers: __Options_CountDownComponent | undefined): void { + + public __updateStruct(initializers: (__Options_CountDownComponent | undefined)): void { if (((({let gensym___188547633 = initializers; (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { this.__backing_count!.update((initializers!.count as number)); } } - private __backing_count?: PropDecoratedVariable; + + private __backing_count?: IPropDecoratedVariable; + public get count(): number { return this.__backing_count!.get(); } + public set count(value: number) { this.__backing_count!.set(value); } + private __backing_costOfOneAttempt?: number; + public get costOfOneAttempt(): number { return (this.__backing_costOfOneAttempt as number); } + public set costOfOneAttempt(value: number) { this.__backing_costOfOneAttempt = value; } - @memo() public _build(@memo() style: ((instance: CountDownComponent)=> CountDownComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_CountDownComponent | undefined): void { - Column(undefined, undefined, (() => { - if (((this.count) > (0))) { - Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); - } else { - Text(undefined, "Game over!", undefined, undefined); - } - Button(@memo() ((instance: UIButtonAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ConditionScope(@memo() (() => { + if (((this.count) > (0))) { + ConditionBranch(@memo() (() => { + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); + })); + } else { + ConditionBranch(@memo() (() => { + Text(undefined, "Game over!", undefined, undefined); + })); + } + })); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.count -= this.costOfOneAttempt; })); @@ -94,32 +116,39 @@ function main() {} }), "Try again", undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class ParentComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ParentComponent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_countDownStartValue = new StateDecoratedVariable("countDownStartValue", ((({let gensym___249912438 = initializers; +@Component() final struct ParentComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_countDownStartValue = STATE_MGMT_FACTORY.makeState(this, "countDownStartValue", ((({let gensym___249912438 = initializers; (((gensym___249912438) == (null)) ? undefined : gensym___249912438.countDownStartValue)})) ?? (10))); } - public __updateStruct(initializers: __Options_ParentComponent | undefined): void {} - private __backing_countDownStartValue?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_countDownStartValue?: IStateDecoratedVariable; + public get countDownStartValue(): number { return this.__backing_countDownStartValue!.get(); } + public set countDownStartValue(value: number) { this.__backing_countDownStartValue!.set(value); } - @memo() public _build(@memo() style: ((instance: ParentComponent)=> ParentComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ParentComponent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue += 1; })); return; }), "+1 - Nuggets in New Game", undefined, undefined); - Button(@memo() ((instance: UIButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue -= 1; })); @@ -127,29 +156,38 @@ function main() {} }), "-1 - Nuggets in New Game", undefined, undefined); CountDownComponent._instantiateImpl(undefined, (() => { return new CountDownComponent(); - }), ({ + }), { count: this.countDownStartValue, costOfOneAttempt: 2, - } as __Options_CountDownComponent), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -interface __Options_CountDownComponent { - set count(count: number | undefined) - get count(): number | undefined - set __backing_count(__backing_count: PropDecoratedVariable | undefined) - get __backing_count(): PropDecoratedVariable | undefined - set costOfOneAttempt(costOfOneAttempt: number | undefined) - get costOfOneAttempt(): number | undefined +@Component() export interface __Options_CountDownComponent { + set count(count: (number | undefined)) + + get count(): (number | undefined) + set __backing_count(__backing_count: (IPropDecoratedVariable | undefined)) + + get __backing_count(): (IPropDecoratedVariable | undefined) + set costOfOneAttempt(costOfOneAttempt: (number | undefined)) + + get costOfOneAttempt(): (number | undefined) + } -interface __Options_ParentComponent { - set countDownStartValue(countDownStartValue: number | undefined) - get countDownStartValue(): number | undefined - set __backing_countDownStartValue(__backing_countDownStartValue: StateDecoratedVariable | undefined) - get __backing_countDownStartValue(): StateDecoratedVariable | undefined +@Component() export interface __Options_ParentComponent { + set countDownStartValue(countDownStartValue: (number | undefined)) + + get countDownStartValue(): (number | undefined) + set __backing_countDownStartValue(__backing_countDownStartValue: (IStateDecoratedVariable | undefined)) + + get __backing_countDownStartValue(): (IStateDecoratedVariable | undefined) + } `; @@ -159,9 +197,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @Prop decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0e7053e3a067934bba68078b6ee5d79d30da74c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-basic-type.test.ts @@ -0,0 +1,216 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consume-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Consume decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +@Component() final struct PropParent extends CustomComponent { + @Consume() public conVar1!: string; + + @Consume() public conVar2!: number; + + @Consume() public conVar3!: boolean; + + @Consume() public conVar4?: undefined; + + @Consume() public conVar5?: null; + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + conVar1?: string; + @Consume() __backing_conVar1?: string; + conVar2?: number; + @Consume() __backing_conVar2?: number; + conVar3?: boolean; + @Consume() __backing_conVar3?: boolean; + conVar4?: undefined; + @Consume() __backing_conVar4?: undefined; + conVar5?: null; + @Consume() __backing_conVar5?: null; + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_conVar1 = STATE_MGMT_FACTORY.makeConsume(this, "conVar1", "conVar1"); + this.__backing_conVar2 = STATE_MGMT_FACTORY.makeConsume(this, "conVar2", "conVar2"); + this.__backing_conVar3 = STATE_MGMT_FACTORY.makeConsume(this, "conVar3", "conVar3"); + this.__backing_conVar4 = STATE_MGMT_FACTORY.makeConsume(this, "conVar4", "conVar4"); + this.__backing_conVar5 = STATE_MGMT_FACTORY.makeConsume(this, "conVar5", "conVar5"); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void {} + + private __backing_conVar1?: IConsumeDecoratedVariable; + + public get conVar1(): string { + return this.__backing_conVar1!.get(); + } + + public set conVar1(value: string) { + this.__backing_conVar1!.set(value); + } + + private __backing_conVar2?: IConsumeDecoratedVariable; + + public get conVar2(): number { + return this.__backing_conVar2!.get(); + } + + public set conVar2(value: number) { + this.__backing_conVar2!.set(value); + } + + private __backing_conVar3?: IConsumeDecoratedVariable; + + public get conVar3(): boolean { + return this.__backing_conVar3!.get(); + } + + public set conVar3(value: boolean) { + this.__backing_conVar3!.set(value); + } + + private __backing_conVar4?: IConsumeDecoratedVariable; + + public get conVar4(): undefined { + return this.__backing_conVar4!.get(); + } + + public set conVar4(value: undefined) { + this.__backing_conVar4!.set(value); + } + + private __backing_conVar5?: IConsumeDecoratedVariable; + + public get conVar5(): null { + return this.__backing_conVar5!.get(); + } + + public set conVar5(value: null) { + this.__backing_conVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set conVar1(conVar1: (string | undefined)) + + get conVar1(): (string | undefined) + set __backing_conVar1(__backing_conVar1: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar1(): (IConsumeDecoratedVariable | undefined) + set conVar2(conVar2: (number | undefined)) + + get conVar2(): (number | undefined) + set __backing_conVar2(__backing_conVar2: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar2(): (IConsumeDecoratedVariable | undefined) + set conVar3(conVar3: (boolean | undefined)) + + get conVar3(): (boolean | undefined) + set __backing_conVar3(__backing_conVar3: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar3(): (IConsumeDecoratedVariable | undefined) + set conVar4(conVar4: (undefined | undefined)) + + get conVar4(): (undefined | undefined) + set __backing_conVar4(__backing_conVar4: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar4(): (IConsumeDecoratedVariable | undefined) + set conVar5(conVar5: (null | undefined)) + + get conVar5(): (null | undefined) + set __backing_conVar5(__backing_conVar5: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar5(): (IConsumeDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Consume decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..202edb7b0ec1bebb19401f90a3f1cbbc7b03980c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/consume-complex-type.test.ts @@ -0,0 +1,476 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consume-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Consume decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component() final struct Parent extends CustomComponent { + @Consume() public conVar1!: Per; + + @Consume() public conVar2!: Array; + + @Consume() public conVar3!: PropType; + + @Consume() public conVar4!: Set; + + @Consume() public conVar5!: boolean[]; + + @Consume() public conVar6!: Array; + + @Consume() public conVar7!: Per[]; + + @Consume() public conVar8!: ((sr: string)=> void); + + @Consume() public conVar9!: Date; + + @Consume() public conVar10!: Map; + + @Consume() public conVar11!: (string | number); + + @Consume() public conVar12!: (Set | Per); + + @Consume() public conVar13?: (Set | null); + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + conVar1?: Per; + @Consume() __backing_conVar1?: Per; + conVar2?: Array; + @Consume() __backing_conVar2?: Array; + conVar3?: PropType; + @Consume() __backing_conVar3?: PropType; + conVar4?: Set; + @Consume() __backing_conVar4?: Set; + conVar5?: boolean[]; + @Consume() __backing_conVar5?: boolean[]; + conVar6?: Array; + @Consume() __backing_conVar6?: Array; + conVar7?: Per[]; + @Consume() __backing_conVar7?: Per[]; + conVar8?: ((sr: string)=> void); + @Consume() __backing_conVar8?: ((sr: string)=> void); + conVar9?: Date; + @Consume() __backing_conVar9?: Date; + conVar10?: Map; + @Consume() __backing_conVar10?: Map; + conVar11?: (string | number); + @Consume() __backing_conVar11?: (string | number); + conVar12?: (Set | Per); + @Consume() __backing_conVar12?: (Set | Per); + conVar13?: (Set | null); + @Consume() __backing_conVar13?: (Set | null); + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Consume as Consume } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class PropType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: PropType = new PropType(0, 0); + + public static readonly TYPE2: PropType = new PropType(1, 1); + + public static readonly TYPE3: PropType = new PropType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + + public getName(): String { + return PropType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): PropType { + for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { + if (((name) == (PropType.#NamesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant PropType.") + (name))); + } + + public static fromValue(value: int): PropType { + for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { + if (((value) == (PropType.#ValuesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum PropType with value ") + (value))); + } + + public valueOf(): int { + return PropType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return PropType.#StringValuesArray[this.#ordinal]; + } + + public static values(): PropType[] { + return PropType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: PropType): String { + return e.getName(); + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_conVar1 = STATE_MGMT_FACTORY.makeConsume(this, "conVar1", "conVar1"); + this.__backing_conVar2 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar2", "conVar2"); + this.__backing_conVar3 = STATE_MGMT_FACTORY.makeConsume(this, "conVar3", "conVar3"); + this.__backing_conVar4 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar4", "conVar4"); + this.__backing_conVar5 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar5", "conVar5"); + this.__backing_conVar6 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar6", "conVar6"); + this.__backing_conVar7 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar7", "conVar7"); + this.__backing_conVar8 = STATE_MGMT_FACTORY.makeConsume<((sr: string)=> void)>(this, "conVar8", "conVar8"); + this.__backing_conVar9 = STATE_MGMT_FACTORY.makeConsume(this, "conVar9", "conVar9"); + this.__backing_conVar10 = STATE_MGMT_FACTORY.makeConsume>(this, "conVar10", "conVar10"); + this.__backing_conVar11 = STATE_MGMT_FACTORY.makeConsume<(string | number)>(this, "conVar11", "conVar11"); + this.__backing_conVar12 = STATE_MGMT_FACTORY.makeConsume<(Set | Per)>(this, "conVar12", "conVar12"); + this.__backing_conVar13 = STATE_MGMT_FACTORY.makeConsume<(Set | null)>(this, "conVar13", "conVar13"); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_conVar1?: IConsumeDecoratedVariable; + + public get conVar1(): Per { + return this.__backing_conVar1!.get(); + } + + public set conVar1(value: Per) { + this.__backing_conVar1!.set(value); + } + + private __backing_conVar2?: IConsumeDecoratedVariable>; + + public get conVar2(): Array { + return this.__backing_conVar2!.get(); + } + + public set conVar2(value: Array) { + this.__backing_conVar2!.set(value); + } + + private __backing_conVar3?: IConsumeDecoratedVariable; + + public get conVar3(): PropType { + return this.__backing_conVar3!.get(); + } + + public set conVar3(value: PropType) { + this.__backing_conVar3!.set(value); + } + + private __backing_conVar4?: IConsumeDecoratedVariable>; + + public get conVar4(): Set { + return this.__backing_conVar4!.get(); + } + + public set conVar4(value: Set) { + this.__backing_conVar4!.set(value); + } + + private __backing_conVar5?: IConsumeDecoratedVariable>; + + public get conVar5(): Array { + return this.__backing_conVar5!.get(); + } + + public set conVar5(value: Array) { + this.__backing_conVar5!.set(value); + } + + private __backing_conVar6?: IConsumeDecoratedVariable>; + + public get conVar6(): Array { + return this.__backing_conVar6!.get(); + } + + public set conVar6(value: Array) { + this.__backing_conVar6!.set(value); + } + + private __backing_conVar7?: IConsumeDecoratedVariable>; + + public get conVar7(): Array { + return this.__backing_conVar7!.get(); + } + + public set conVar7(value: Array) { + this.__backing_conVar7!.set(value); + } + + private __backing_conVar8?: IConsumeDecoratedVariable<((sr: string)=> void)>; + + public get conVar8(): ((sr: string)=> void) { + return this.__backing_conVar8!.get(); + } + + public set conVar8(value: ((sr: string)=> void)) { + this.__backing_conVar8!.set(value); + } + + private __backing_conVar9?: IConsumeDecoratedVariable; + + public get conVar9(): Date { + return this.__backing_conVar9!.get(); + } + + public set conVar9(value: Date) { + this.__backing_conVar9!.set(value); + } + + private __backing_conVar10?: IConsumeDecoratedVariable>; + + public get conVar10(): Map { + return this.__backing_conVar10!.get(); + } + + public set conVar10(value: Map) { + this.__backing_conVar10!.set(value); + } + + private __backing_conVar11?: IConsumeDecoratedVariable<(string | number)>; + + public get conVar11(): (string | number) { + return this.__backing_conVar11!.get(); + } + + public set conVar11(value: (string | number)) { + this.__backing_conVar11!.set(value); + } + + private __backing_conVar12?: IConsumeDecoratedVariable<(Set | Per)>; + + public get conVar12(): (Set | Per) { + return this.__backing_conVar12!.get(); + } + + public set conVar12(value: (Set | Per)) { + this.__backing_conVar12!.set(value); + } + + private __backing_conVar13?: IConsumeDecoratedVariable<(Set | null)>; + + public get conVar13(): (Set | null) { + return this.__backing_conVar13!.get(); + } + + public set conVar13(value: (Set | null)) { + this.__backing_conVar13!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set conVar1(conVar1: (Per | undefined)) + + get conVar1(): (Per | undefined) + set __backing_conVar1(__backing_conVar1: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar1(): (IConsumeDecoratedVariable | undefined) + set conVar2(conVar2: (Array | undefined)) + + get conVar2(): (Array | undefined) + set __backing_conVar2(__backing_conVar2: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar2(): (IConsumeDecoratedVariable> | undefined) + set conVar3(conVar3: (PropType | undefined)) + + get conVar3(): (PropType | undefined) + set __backing_conVar3(__backing_conVar3: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar3(): (IConsumeDecoratedVariable | undefined) + set conVar4(conVar4: (Set | undefined)) + + get conVar4(): (Set | undefined) + set __backing_conVar4(__backing_conVar4: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar4(): (IConsumeDecoratedVariable> | undefined) + set conVar5(conVar5: (Array | undefined)) + + get conVar5(): (Array | undefined) + set __backing_conVar5(__backing_conVar5: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar5(): (IConsumeDecoratedVariable> | undefined) + set conVar6(conVar6: (Array | undefined)) + + get conVar6(): (Array | undefined) + set __backing_conVar6(__backing_conVar6: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar6(): (IConsumeDecoratedVariable> | undefined) + set conVar7(conVar7: (Array | undefined)) + + get conVar7(): (Array | undefined) + set __backing_conVar7(__backing_conVar7: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar7(): (IConsumeDecoratedVariable> | undefined) + set conVar8(conVar8: (((sr: string)=> void) | undefined)) + + get conVar8(): (((sr: string)=> void) | undefined) + set __backing_conVar8(__backing_conVar8: (IConsumeDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_conVar8(): (IConsumeDecoratedVariable<((sr: string)=> void)> | undefined) + set conVar9(conVar9: (Date | undefined)) + + get conVar9(): (Date | undefined) + set __backing_conVar9(__backing_conVar9: (IConsumeDecoratedVariable | undefined)) + + get __backing_conVar9(): (IConsumeDecoratedVariable | undefined) + set conVar10(conVar10: (Map | undefined)) + + get conVar10(): (Map | undefined) + set __backing_conVar10(__backing_conVar10: (IConsumeDecoratedVariable> | undefined)) + + get __backing_conVar10(): (IConsumeDecoratedVariable> | undefined) + set conVar11(conVar11: ((string | number) | undefined)) + + get conVar11(): ((string | number) | undefined) + set __backing_conVar11(__backing_conVar11: (IConsumeDecoratedVariable<(string | number)> | undefined)) + + get __backing_conVar11(): (IConsumeDecoratedVariable<(string | number)> | undefined) + set conVar12(conVar12: ((Set | Per) | undefined)) + + get conVar12(): ((Set | Per) | undefined) + set __backing_conVar12(__backing_conVar12: (IConsumeDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_conVar12(): (IConsumeDecoratedVariable<(Set | Per)> | undefined) + set conVar13(conVar13: ((Set | null) | undefined)) + + get conVar13(): ((Set | null) | undefined) + set __backing_conVar13(__backing_conVar13: (IConsumeDecoratedVariable<(Set | null)> | undefined)) + + get __backing_conVar13(): (IConsumeDecoratedVariable<(Set | null)> | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Consume decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts index 9f57877626b1b3aa3a18de0419f718f1971bf41d..195bb2726f284136c55af59e171f7baf0e25495a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,129 +38,179 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Ancestors extends CustomComponent { - public __initializeStruct(initializers: __Options_Ancestors | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_count = this.addProvidedVar("count", "count", ((({let gensym___58710805 = initializers; +@Component() final struct Ancestors extends CustomComponent { + public __initializeStruct(initializers: (__Options_Ancestors | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count", "count", ((({let gensym___58710805 = initializers; (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? ("Child0")), false); - this.__backing_count1 = this.addProvidedVar("count1", "prov1", ((({let gensym___84874570 = initializers; + this.__backing_count1 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count1", "prov1", ((({let gensym___84874570 = initializers; (((gensym___84874570) == (null)) ? undefined : gensym___84874570.count1)})) ?? ("Child1")), false); - this.__backing_count2 = this.addProvidedVar("count2", "prov2", ((({let gensym___124037738 = initializers; + this.__backing_count2 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count2", "prov2", ((({let gensym___124037738 = initializers; (((gensym___124037738) == (null)) ? undefined : gensym___124037738.count2)})) ?? ("Child2")), false); - this.__backing_count3 = this.addProvidedVar("count3", "prov3", ((({let gensym___199202238 = initializers; + this.__backing_count3 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count3", "prov3", ((({let gensym___199202238 = initializers; (((gensym___199202238) == (null)) ? undefined : gensym___199202238.count3)})) ?? ("Child3")), true); - this.__backing_count4 = this.addProvidedVar("count4", "count4", ((({let gensym___4359740 = initializers; + this.__backing_count4 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count4", "count4", ((({let gensym___4359740 = initializers; (((gensym___4359740) == (null)) ? undefined : gensym___4359740.count4)})) ?? ("Child4")), false); - this.__backing_count5 = this.addProvidedVar("count5", "count5", ((({let gensym___208755050 = initializers; + this.__backing_count5 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count5", "count5", ((({let gensym___208755050 = initializers; (((gensym___208755050) == (null)) ? undefined : gensym___208755050.count5)})) ?? ("Child5")), true); - this.__backing_count6 = this.addProvidedVar("count6", "", ((({let gensym___37571585 = initializers; + this.__backing_count6 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count6", "", ((({let gensym___37571585 = initializers; (((gensym___37571585) == (null)) ? undefined : gensym___37571585.count6)})) ?? ("Child6")), true); - this.__backing_count7 = this.addProvidedVar("count7", "", ((({let gensym___2162781 = initializers; + this.__backing_count7 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count7", "", ((({let gensym___2162781 = initializers; (((gensym___2162781) == (null)) ? undefined : gensym___2162781.count7)})) ?? ("Child7")), false); } - public __updateStruct(initializers: __Options_Ancestors | undefined): void {} - private __backing_count?: ProvideDecoratedVariable; - public get count(): string | undefined { + + public __updateStruct(initializers: (__Options_Ancestors | undefined)): void {} + + private __backing_count?: IProvideDecoratedVariable<(string | undefined)>; + + public get count(): (string | undefined) { return this.__backing_count!.get(); } - public set count(value: string | undefined) { + + public set count(value: (string | undefined)) { this.__backing_count!.set(value); } - private __backing_count1?: ProvideDecoratedVariable; - public get count1(): string | undefined { + + private __backing_count1?: IProvideDecoratedVariable<(string | undefined)>; + + public get count1(): (string | undefined) { return this.__backing_count1!.get(); } - public set count1(value: string | undefined) { + + public set count1(value: (string | undefined)) { this.__backing_count1!.set(value); } - private __backing_count2?: ProvideDecoratedVariable; - public get count2(): string | undefined { + + private __backing_count2?: IProvideDecoratedVariable<(string | undefined)>; + + public get count2(): (string | undefined) { return this.__backing_count2!.get(); } - public set count2(value: string | undefined) { + + public set count2(value: (string | undefined)) { this.__backing_count2!.set(value); } - private __backing_count3?: ProvideDecoratedVariable; - public get count3(): string | undefined { + + private __backing_count3?: IProvideDecoratedVariable<(string | undefined)>; + + public get count3(): (string | undefined) { return this.__backing_count3!.get(); } - public set count3(value: string | undefined) { + + public set count3(value: (string | undefined)) { this.__backing_count3!.set(value); } - private __backing_count4?: ProvideDecoratedVariable; - public get count4(): string | undefined { + + private __backing_count4?: IProvideDecoratedVariable<(string | undefined)>; + + public get count4(): (string | undefined) { return this.__backing_count4!.get(); } - public set count4(value: string | undefined) { + + public set count4(value: (string | undefined)) { this.__backing_count4!.set(value); } - private __backing_count5?: ProvideDecoratedVariable; - public get count5(): string | undefined { + + private __backing_count5?: IProvideDecoratedVariable<(string | undefined)>; + + public get count5(): (string | undefined) { return this.__backing_count5!.get(); } - public set count5(value: string | undefined) { + + public set count5(value: (string | undefined)) { this.__backing_count5!.set(value); } - private __backing_count6?: ProvideDecoratedVariable; - public get count6(): string | undefined { + + private __backing_count6?: IProvideDecoratedVariable<(string | undefined)>; + + public get count6(): (string | undefined) { return this.__backing_count6!.get(); } - public set count6(value: string | undefined) { + + public set count6(value: (string | undefined)) { this.__backing_count6!.set(value); } - private __backing_count7?: ProvideDecoratedVariable; - public get count7(): string | undefined { + + private __backing_count7?: IProvideDecoratedVariable<(string | undefined)>; + + public get count7(): (string | undefined) { return this.__backing_count7!.get(); } - public set count7(value: string | undefined) { + + public set count7(value: (string | undefined)) { this.__backing_count7!.set(value); } - @memo() public _build(@memo() style: ((instance: Ancestors)=> Ancestors) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Ancestors | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Ancestors { - set count(count: string | undefined | undefined) - get count(): string | undefined | undefined - set __backing_count(__backing_count: ProvideDecoratedVariable | undefined) - get __backing_count(): ProvideDecoratedVariable | undefined - set count1(count1: string | undefined | undefined) - get count1(): string | undefined | undefined - set __backing_count1(__backing_count1: ProvideDecoratedVariable | undefined) - get __backing_count1(): ProvideDecoratedVariable | undefined - set count2(count2: string | undefined | undefined) - get count2(): string | undefined | undefined - set __backing_count2(__backing_count2: ProvideDecoratedVariable | undefined) - get __backing_count2(): ProvideDecoratedVariable | undefined - set count3(count3: string | undefined | undefined) - get count3(): string | undefined | undefined - set __backing_count3(__backing_count3: ProvideDecoratedVariable | undefined) - get __backing_count3(): ProvideDecoratedVariable | undefined - set count4(count4: string | undefined | undefined) - get count4(): string | undefined | undefined - set __backing_count4(__backing_count4: ProvideDecoratedVariable | undefined) - get __backing_count4(): ProvideDecoratedVariable | undefined - set count5(count5: string | undefined | undefined) - get count5(): string | undefined | undefined - set __backing_count5(__backing_count5: ProvideDecoratedVariable | undefined) - get __backing_count5(): ProvideDecoratedVariable | undefined - set count6(count6: string | undefined | undefined) - get count6(): string | undefined | undefined - set __backing_count6(__backing_count6: ProvideDecoratedVariable | undefined) - get __backing_count6(): ProvideDecoratedVariable | undefined - set count7(count7: string | undefined | undefined) - get count7(): string | undefined | undefined - set __backing_count7(__backing_count7: ProvideDecoratedVariable | undefined) - get __backing_count7(): ProvideDecoratedVariable | undefined +@Component() export interface __Options_Ancestors { + set count(count: ((string | undefined) | undefined)) + + get count(): ((string | undefined) | undefined) + set __backing_count(__backing_count: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count1(count1: ((string | undefined) | undefined)) + + get count1(): ((string | undefined) | undefined) + set __backing_count1(__backing_count1: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count1(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count2(count2: ((string | undefined) | undefined)) + + get count2(): ((string | undefined) | undefined) + set __backing_count2(__backing_count2: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count2(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count3(count3: ((string | undefined) | undefined)) + + get count3(): ((string | undefined) | undefined) + set __backing_count3(__backing_count3: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count3(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count4(count4: ((string | undefined) | undefined)) + + get count4(): ((string | undefined) | undefined) + set __backing_count4(__backing_count4: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count4(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count5(count5: ((string | undefined) | undefined)) + + get count5(): ((string | undefined) | undefined) + set __backing_count5(__backing_count5: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count5(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count6(count6: ((string | undefined) | undefined)) + + get count6(): ((string | undefined) | undefined) + set __backing_count6(__backing_count6: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count6(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + set count7(count7: ((string | undefined) | undefined)) + + get count7(): ((string | undefined) | undefined) + set __backing_count7(__backing_count7: (IProvideDecoratedVariable<(string | undefined)> | undefined)) + + get __backing_count7(): (IProvideDecoratedVariable<(string | undefined)> | undefined) + } `; @@ -169,9 +220,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test different @Provide annotation usage transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts index dffbb429f3593481a71463a651ede773270191d5..f0fefa008a5eea8e371c75f833f0bb7a0dd731df 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,90 +38,125 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class PropParent extends CustomComponent { - public __initializeStruct(initializers: __Options_PropParent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_provideVar1 = this.addProvidedVar("provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_provideVar1 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; (((gensym___181030638) == (null)) ? undefined : gensym___181030638.provideVar1)})) ?? ("propVar1")), false); - this.__backing_provideVar2 = this.addProvidedVar("provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; + this.__backing_provideVar2 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; (((gensym___143944235) == (null)) ? undefined : gensym___143944235.provideVar2)})) ?? (50)), false); - this.__backing_provideVar3 = this.addProvidedVar("provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; + this.__backing_provideVar3 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; (((gensym___262195977) == (null)) ? undefined : gensym___262195977.provideVar3)})) ?? (true)), false); - this.__backing_provideVar4 = this.addProvidedVar("provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; + this.__backing_provideVar4 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; (((gensym___85711435) == (null)) ? undefined : gensym___85711435.provideVar4)})) ?? (undefined)), false); - this.__backing_provideVar5 = this.addProvidedVar("provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; + this.__backing_provideVar5 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; (((gensym___139253630) == (null)) ? undefined : gensym___139253630.provideVar5)})) ?? (null)), false); } - public __updateStruct(initializers: __Options_PropParent | undefined): void {} - private __backing_provideVar1?: ProvideDecoratedVariable; + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void {} + + private __backing_provideVar1?: IProvideDecoratedVariable; + public get provideVar1(): string { return this.__backing_provideVar1!.get(); } + public set provideVar1(value: string) { this.__backing_provideVar1!.set(value); } - private __backing_provideVar2?: ProvideDecoratedVariable; + + private __backing_provideVar2?: IProvideDecoratedVariable; + public get provideVar2(): number { return this.__backing_provideVar2!.get(); } + public set provideVar2(value: number) { this.__backing_provideVar2!.set(value); } - private __backing_provideVar3?: ProvideDecoratedVariable; + + private __backing_provideVar3?: IProvideDecoratedVariable; + public get provideVar3(): boolean { return this.__backing_provideVar3!.get(); } + public set provideVar3(value: boolean) { this.__backing_provideVar3!.set(value); } - private __backing_provideVar4?: ProvideDecoratedVariable; + + private __backing_provideVar4?: IProvideDecoratedVariable; + public get provideVar4(): undefined { return this.__backing_provideVar4!.get(); } + public set provideVar4(value: undefined) { this.__backing_provideVar4!.set(value); } - private __backing_provideVar5?: ProvideDecoratedVariable; + + private __backing_provideVar5?: IProvideDecoratedVariable; + public get provideVar5(): null { return this.__backing_provideVar5!.get(); } + public set provideVar5(value: null) { this.__backing_provideVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: PropParent)=> PropParent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_PropParent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_PropParent { - set provideVar1(provideVar1: string | undefined) - get provideVar1(): string | undefined - set __backing_provideVar1(__backing_provideVar1: ProvideDecoratedVariable | undefined) - get __backing_provideVar1(): ProvideDecoratedVariable | undefined - set provideVar2(provideVar2: number | undefined) - get provideVar2(): number | undefined - set __backing_provideVar2(__backing_provideVar2: ProvideDecoratedVariable | undefined) - get __backing_provideVar2(): ProvideDecoratedVariable | undefined - set provideVar3(provideVar3: boolean | undefined) - get provideVar3(): boolean | undefined - set __backing_provideVar3(__backing_provideVar3: ProvideDecoratedVariable | undefined) - get __backing_provideVar3(): ProvideDecoratedVariable | undefined - set provideVar4(provideVar4: undefined | undefined) - get provideVar4(): undefined | undefined - set __backing_provideVar4(__backing_provideVar4: ProvideDecoratedVariable | undefined) - get __backing_provideVar4(): ProvideDecoratedVariable | undefined - set provideVar5(provideVar5: null | undefined) - get provideVar5(): null | undefined - set __backing_provideVar5(__backing_provideVar5: ProvideDecoratedVariable | undefined) - get __backing_provideVar5(): ProvideDecoratedVariable | undefined +@Component() export interface __Options_PropParent { + set provideVar1(provideVar1: (string | undefined)) + + get provideVar1(): (string | undefined) + set __backing_provideVar1(__backing_provideVar1: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar1(): (IProvideDecoratedVariable | undefined) + set provideVar2(provideVar2: (number | undefined)) + + get provideVar2(): (number | undefined) + set __backing_provideVar2(__backing_provideVar2: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar2(): (IProvideDecoratedVariable | undefined) + set provideVar3(provideVar3: (boolean | undefined)) + + get provideVar3(): (boolean | undefined) + set __backing_provideVar3(__backing_provideVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar3(): (IProvideDecoratedVariable | undefined) + set provideVar4(provideVar4: (undefined | undefined)) + + get provideVar4(): (undefined | undefined) + set __backing_provideVar4(__backing_provideVar4: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar4(): (IProvideDecoratedVariable | undefined) + set provideVar5(provideVar5: (null | undefined)) + + get provideVar5(): (null | undefined) + set __backing_provideVar5(__backing_provideVar5: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar5(): (IProvideDecoratedVariable | undefined) + } `; @@ -130,9 +166,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @Provide decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts index 6684a83fe1e9ce59451fe32b83243e045150e5c3..15967cd3b6137333d3f93479961ee7d1baf90888 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { Provide as Provide } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class PropType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: PropType = new PropType(0, 0); + public static readonly TYPE2: PropType = new PropType(1, 1); + public static readonly TYPE3: PropType = new PropType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + public getName(): String { return PropType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): PropType { for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { if (((name) == (PropType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class PropType extends BaseEnum { } throw new Error((("No enum constant PropType.") + (name))); } + public static fromValue(value: int): PropType { for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { if (((value) == (PropType.#ValuesArray[i]))) { @@ -87,188 +107,259 @@ final class PropType extends BaseEnum { } throw new Error((("No enum PropType with value ") + (value))); } + public valueOf(): int { return PropType.#ValuesArray[this.#ordinal]; } + public toString(): String { return PropType.#StringValuesArray[this.#ordinal]; } + public static values(): PropType[] { return PropType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: PropType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_provideVar1 = this.addProvidedVar("provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_provideVar1 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar1", "provideVar1", ((({let gensym___181030638 = initializers; (((gensym___181030638) == (null)) ? undefined : gensym___181030638.provideVar1)})) ?? (new Per(6))), false); - this.__backing_provideVar2 = this.addProvidedVar>("provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; + this.__backing_provideVar2 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; (((gensym___143944235) == (null)) ? undefined : gensym___143944235.provideVar2)})) ?? (new Array(3, 6, 8))), false); - this.__backing_provideVar3 = this.addProvidedVar("provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; + this.__backing_provideVar3 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; (((gensym___262195977) == (null)) ? undefined : gensym___262195977.provideVar3)})) ?? (PropType.TYPE3)), false); - this.__backing_provideVar4 = this.addProvidedVar>("provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; + this.__backing_provideVar4 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; (((gensym___85711435) == (null)) ? undefined : gensym___85711435.provideVar4)})) ?? (new Set(new Array("aa", "bb")))), false); - this.__backing_provideVar5 = this.addProvidedVar>("provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; + this.__backing_provideVar5 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; (((gensym___139253630) == (null)) ? undefined : gensym___139253630.provideVar5)})) ?? ([true, false])), false); - this.__backing_provideVar6 = this.addProvidedVar>("provideVar6", "provideVar6", ((({let gensym___146872112 = initializers; + this.__backing_provideVar6 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar6", "provideVar6", ((({let gensym___146872112 = initializers; (((gensym___146872112) == (null)) ? undefined : gensym___146872112.provideVar6)})) ?? (new Array(new Per(7), new Per(11)))), false); - this.__backing_provideVar7 = this.addProvidedVar>("provideVar7", "provideVar7", ((({let gensym___174412117 = initializers; + this.__backing_provideVar7 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar7", "provideVar7", ((({let gensym___174412117 = initializers; (((gensym___174412117) == (null)) ? undefined : gensym___174412117.provideVar7)})) ?? ([new Per(7), new Per(11)])), false); - this.__backing_provideVar8 = this.addProvidedVar<((sr: string)=> void)>("provideVar8", "provideVar8", ((({let gensym___253467853 = initializers; + this.__backing_provideVar8 = STATE_MGMT_FACTORY.makeProvide<((sr: string)=> void)>(this, "provideVar8", "provideVar8", ((({let gensym___253467853 = initializers; (((gensym___253467853) == (null)) ? undefined : gensym___253467853.provideVar8)})) ?? (((sr: string) => {}))), false); - this.__backing_provideVar9 = this.addProvidedVar("provideVar9", "provideVar9", ((({let gensym___179115605 = initializers; + this.__backing_provideVar9 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar9", "provideVar9", ((({let gensym___179115605 = initializers; (((gensym___179115605) == (null)) ? undefined : gensym___179115605.provideVar9)})) ?? (new Date("2025-4-23"))), false); - this.__backing_provideVar10 = this.addProvidedVar>("provideVar10", "provideVar10", ((({let gensym___209671248 = initializers; + this.__backing_provideVar10 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar10", "provideVar10", ((({let gensym___209671248 = initializers; (((gensym___209671248) == (null)) ? undefined : gensym___209671248.provideVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]]))), false); - this.__backing_provideVar11 = this.addProvidedVar("provideVar11", "provideVar11", ((({let gensym___150211849 = initializers; + this.__backing_provideVar11 = STATE_MGMT_FACTORY.makeProvide<(string | number)>(this, "provideVar11", "provideVar11", ((({let gensym___150211849 = initializers; (((gensym___150211849) == (null)) ? undefined : gensym___150211849.provideVar11)})) ?? (0.0)), false); - this.__backing_provideVar12 = this.addProvidedVar | Per>("provideVar12", "provideVar12", ((({let gensym___256025818 = initializers; + this.__backing_provideVar12 = STATE_MGMT_FACTORY.makeProvide<(Set | Per)>(this, "provideVar12", "provideVar12", ((({let gensym___256025818 = initializers; (((gensym___256025818) == (null)) ? undefined : gensym___256025818.provideVar12)})) ?? (new Per(6))), false); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_provideVar1?: ProvideDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_provideVar1?: IProvideDecoratedVariable; + public get provideVar1(): Per { return this.__backing_provideVar1!.get(); } + public set provideVar1(value: Per) { this.__backing_provideVar1!.set(value); } - private __backing_provideVar2?: ProvideDecoratedVariable>; + + private __backing_provideVar2?: IProvideDecoratedVariable>; + public get provideVar2(): Array { return this.__backing_provideVar2!.get(); } + public set provideVar2(value: Array) { this.__backing_provideVar2!.set(value); } - private __backing_provideVar3?: ProvideDecoratedVariable; + + private __backing_provideVar3?: IProvideDecoratedVariable; + public get provideVar3(): PropType { return this.__backing_provideVar3!.get(); } + public set provideVar3(value: PropType) { this.__backing_provideVar3!.set(value); } - private __backing_provideVar4?: ProvideDecoratedVariable>; + + private __backing_provideVar4?: IProvideDecoratedVariable>; + public get provideVar4(): Set { return this.__backing_provideVar4!.get(); } + public set provideVar4(value: Set) { this.__backing_provideVar4!.set(value); } - private __backing_provideVar5?: ProvideDecoratedVariable>; + + private __backing_provideVar5?: IProvideDecoratedVariable>; + public get provideVar5(): Array { return this.__backing_provideVar5!.get(); } + public set provideVar5(value: Array) { this.__backing_provideVar5!.set(value); } - private __backing_provideVar6?: ProvideDecoratedVariable>; + + private __backing_provideVar6?: IProvideDecoratedVariable>; + public get provideVar6(): Array { return this.__backing_provideVar6!.get(); } + public set provideVar6(value: Array) { this.__backing_provideVar6!.set(value); } - private __backing_provideVar7?: ProvideDecoratedVariable>; + + private __backing_provideVar7?: IProvideDecoratedVariable>; + public get provideVar7(): Array { return this.__backing_provideVar7!.get(); } + public set provideVar7(value: Array) { this.__backing_provideVar7!.set(value); } - private __backing_provideVar8?: ProvideDecoratedVariable<((sr: string)=> void)>; + + private __backing_provideVar8?: IProvideDecoratedVariable<((sr: string)=> void)>; + public get provideVar8(): ((sr: string)=> void) { return this.__backing_provideVar8!.get(); } + public set provideVar8(value: ((sr: string)=> void)) { this.__backing_provideVar8!.set(value); } - private __backing_provideVar9?: ProvideDecoratedVariable; + + private __backing_provideVar9?: IProvideDecoratedVariable; + public get provideVar9(): Date { return this.__backing_provideVar9!.get(); } + public set provideVar9(value: Date) { this.__backing_provideVar9!.set(value); } - private __backing_provideVar10?: ProvideDecoratedVariable>; + + private __backing_provideVar10?: IProvideDecoratedVariable>; + public get provideVar10(): Map { return this.__backing_provideVar10!.get(); } + public set provideVar10(value: Map) { this.__backing_provideVar10!.set(value); } - private __backing_provideVar11?: ProvideDecoratedVariable; - public get provideVar11(): string | number { + + private __backing_provideVar11?: IProvideDecoratedVariable<(string | number)>; + + public get provideVar11(): (string | number) { return this.__backing_provideVar11!.get(); } - public set provideVar11(value: string | number) { + + public set provideVar11(value: (string | number)) { this.__backing_provideVar11!.set(value); } - private __backing_provideVar12?: ProvideDecoratedVariable | Per>; - public get provideVar12(): Set | Per { + + private __backing_provideVar12?: IProvideDecoratedVariable<(Set | Per)>; + + public get provideVar12(): (Set | Per) { return this.__backing_provideVar12!.get(); } - public set provideVar12(value: Set | Per) { + + public set provideVar12(value: (Set | Per)) { this.__backing_provideVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set provideVar1(provideVar1: Per | undefined) - get provideVar1(): Per | undefined - set __backing_provideVar1(__backing_provideVar1: ProvideDecoratedVariable | undefined) - get __backing_provideVar1(): ProvideDecoratedVariable | undefined - set provideVar2(provideVar2: Array | undefined) - get provideVar2(): Array | undefined - set __backing_provideVar2(__backing_provideVar2: ProvideDecoratedVariable> | undefined) - get __backing_provideVar2(): ProvideDecoratedVariable> | undefined - set provideVar3(provideVar3: PropType | undefined) - get provideVar3(): PropType | undefined - set __backing_provideVar3(__backing_provideVar3: ProvideDecoratedVariable | undefined) - get __backing_provideVar3(): ProvideDecoratedVariable | undefined - set provideVar4(provideVar4: Set | undefined) - get provideVar4(): Set | undefined - set __backing_provideVar4(__backing_provideVar4: ProvideDecoratedVariable> | undefined) - get __backing_provideVar4(): ProvideDecoratedVariable> | undefined - set provideVar5(provideVar5: Array | undefined) - get provideVar5(): Array | undefined - set __backing_provideVar5(__backing_provideVar5: ProvideDecoratedVariable> | undefined) - get __backing_provideVar5(): ProvideDecoratedVariable> | undefined - set provideVar6(provideVar6: Array | undefined) - get provideVar6(): Array | undefined - set __backing_provideVar6(__backing_provideVar6: ProvideDecoratedVariable> | undefined) - get __backing_provideVar6(): ProvideDecoratedVariable> | undefined - set provideVar7(provideVar7: Array | undefined) - get provideVar7(): Array | undefined - set __backing_provideVar7(__backing_provideVar7: ProvideDecoratedVariable> | undefined) - get __backing_provideVar7(): ProvideDecoratedVariable> | undefined - set provideVar8(provideVar8: ((sr: string)=> void) | undefined) - get provideVar8(): ((sr: string)=> void) | undefined - set __backing_provideVar8(__backing_provideVar8: ProvideDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_provideVar8(): ProvideDecoratedVariable<((sr: string)=> void)> | undefined - set provideVar9(provideVar9: Date | undefined) - get provideVar9(): Date | undefined - set __backing_provideVar9(__backing_provideVar9: ProvideDecoratedVariable | undefined) - get __backing_provideVar9(): ProvideDecoratedVariable | undefined - set provideVar10(provideVar10: Map | undefined) - get provideVar10(): Map | undefined - set __backing_provideVar10(__backing_provideVar10: ProvideDecoratedVariable> | undefined) - get __backing_provideVar10(): ProvideDecoratedVariable> | undefined - set provideVar11(provideVar11: string | number | undefined) - get provideVar11(): string | number | undefined - set __backing_provideVar11(__backing_provideVar11: ProvideDecoratedVariable | undefined) - get __backing_provideVar11(): ProvideDecoratedVariable | undefined - set provideVar12(provideVar12: Set | Per | undefined) - get provideVar12(): Set | Per | undefined - set __backing_provideVar12(__backing_provideVar12: ProvideDecoratedVariable | Per> | undefined) - get __backing_provideVar12(): ProvideDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set provideVar1(provideVar1: (Per | undefined)) + + get provideVar1(): (Per | undefined) + set __backing_provideVar1(__backing_provideVar1: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar1(): (IProvideDecoratedVariable | undefined) + set provideVar2(provideVar2: (Array | undefined)) + + get provideVar2(): (Array | undefined) + set __backing_provideVar2(__backing_provideVar2: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar2(): (IProvideDecoratedVariable> | undefined) + set provideVar3(provideVar3: (PropType | undefined)) + + get provideVar3(): (PropType | undefined) + set __backing_provideVar3(__backing_provideVar3: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar3(): (IProvideDecoratedVariable | undefined) + set provideVar4(provideVar4: (Set | undefined)) + + get provideVar4(): (Set | undefined) + set __backing_provideVar4(__backing_provideVar4: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar4(): (IProvideDecoratedVariable> | undefined) + set provideVar5(provideVar5: (Array | undefined)) + + get provideVar5(): (Array | undefined) + set __backing_provideVar5(__backing_provideVar5: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar5(): (IProvideDecoratedVariable> | undefined) + set provideVar6(provideVar6: (Array | undefined)) + + get provideVar6(): (Array | undefined) + set __backing_provideVar6(__backing_provideVar6: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar6(): (IProvideDecoratedVariable> | undefined) + set provideVar7(provideVar7: (Array | undefined)) + + get provideVar7(): (Array | undefined) + set __backing_provideVar7(__backing_provideVar7: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar7(): (IProvideDecoratedVariable> | undefined) + set provideVar8(provideVar8: (((sr: string)=> void) | undefined)) + + get provideVar8(): (((sr: string)=> void) | undefined) + set __backing_provideVar8(__backing_provideVar8: (IProvideDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_provideVar8(): (IProvideDecoratedVariable<((sr: string)=> void)> | undefined) + set provideVar9(provideVar9: (Date | undefined)) + + get provideVar9(): (Date | undefined) + set __backing_provideVar9(__backing_provideVar9: (IProvideDecoratedVariable | undefined)) + + get __backing_provideVar9(): (IProvideDecoratedVariable | undefined) + set provideVar10(provideVar10: (Map | undefined)) + + get provideVar10(): (Map | undefined) + set __backing_provideVar10(__backing_provideVar10: (IProvideDecoratedVariable> | undefined)) + + get __backing_provideVar10(): (IProvideDecoratedVariable> | undefined) + set provideVar11(provideVar11: ((string | number) | undefined)) + + get provideVar11(): ((string | number) | undefined) + set __backing_provideVar11(__backing_provideVar11: (IProvideDecoratedVariable<(string | number)> | undefined)) + + get __backing_provideVar11(): (IProvideDecoratedVariable<(string | number)> | undefined) + set provideVar12(provideVar12: ((Set | Per) | undefined)) + + get provideVar12(): ((Set | Per) | undefined) + set __backing_provideVar12(__backing_provideVar12: (IProvideDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_provideVar12(): (IProvideDecoratedVariable<(Set | Per)> | undefined) + } `; @@ -278,9 +369,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @Provide decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f01659b47fb9c3a5f348a576164543dcaf2a2a3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-to-consume.test.ts @@ -0,0 +1,250 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provide-to-consume.ets'), +]; + +const pluginTester = new PluginTester('test usage of @Provide and @Consume decorator', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Consume as Consume, Provide as Provide } from "@ohos.arkui.stateManagement"; + +@Component() final struct Child extends CustomComponent { + @Consume() public num!: number; + + @Consume({value:"ss"}) public str!: string; + + public build() { + Column(){ + Text(\`Child num: \${this.num}\`); + Text(\`Child str: \${this.str}\`); + }; + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + @Provide({alias:"num"}) public num: number = 10; + + @Provide({alias:"ss"}) public str: string = "hello"; + + public build() { + Column(){ + Text(\`Parent num: \${this.num}\`); + Text(\`Parent str: \${this.str}\`); + Child(); + }; + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + num?: number; + @Consume() __backing_num?: number; + str?: string; + @Consume({value:"ss"}) __backing_str?: string; + +} + +@Component() export interface __Options_Parent { + num?: number; + @Provide({alias:"num"}) __backing_num?: number; + str?: string; + @Provide({alias:"ss"}) __backing_str?: string; + +} +`; + +const expectedCheckedScript: string = ` +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + +import { Consume as Consume, Provide as Provide } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeConsume(this, "num", "num"); + this.__backing_str = STATE_MGMT_FACTORY.makeConsume(this, "str", "ss"); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_num?: IConsumeDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + + private __backing_str?: IConsumeDecoratedVariable; + + public get str(): string { + return this.__backing_str!.get(); + } + + public set str(value: string) { + this.__backing_str!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Child num: \${this.num}\`, undefined, undefined); + Text(undefined, \`Child str: \${this.str}\`, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeProvide(this, "num", "num", ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (10)), false); + this.__backing_str = STATE_MGMT_FACTORY.makeProvide(this, "str", "ss", ((({let gensym___249074315 = initializers; + (((gensym___249074315) == (null)) ? undefined : gensym___249074315.str)})) ?? ("hello")), false); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_num?: IProvideDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + + private __backing_str?: IProvideDecoratedVariable; + + public get str(): string { + return this.__backing_str!.get(); + } + + public set str(value: string) { + this.__backing_str!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`Parent num: \${this.num}\`, undefined, undefined); + Text(undefined, \`Parent str: \${this.str}\`, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Child { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IConsumeDecoratedVariable | undefined)) + + get __backing_num(): (IConsumeDecoratedVariable | undefined) + set str(str: (string | undefined)) + + get str(): (string | undefined) + set __backing_str(__backing_str: (IConsumeDecoratedVariable | undefined)) + + get __backing_str(): (IConsumeDecoratedVariable | undefined) + +} + +@Component() export interface __Options_Parent { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IProvideDecoratedVariable | undefined)) + + get __backing_num(): (IProvideDecoratedVariable | undefined) + set str(str: (string | undefined)) + + get str(): (string | undefined) + set __backing_str(__backing_str: (IProvideDecoratedVariable | undefined)) + + get __backing_str(): (IProvideDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test usage of @Provide and @Consume decorator', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c4e62f2d14c537b47084e553251f1421b37a07e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-basic-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consumer-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Consumer decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Consumer as Consumer } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_consumerVar1 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar1", "consumerVar1", "propVar1"); + this.__backing_consumerVar2 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar2", "consumerVar2", 50); + this.__backing_consumerVar3 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar3", "consumerVar3", true); + this.__backing_consumerVar4 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar4", "consumerVar4", undefined); + this.__backing_consumerVar5 = STATE_MGMT_FACTORY.makeConsumer(this, "consumerVar5", "consumerVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_consumerVar1?: IConsumerDecoratedVariable; + + public get consumerVar1(): string { + return this.__backing_consumerVar1!.get(); + } + + public set consumerVar1(value: string) { + this.__backing_consumerVar1!.set(value); + } + + private __backing_consumerVar2?: IConsumerDecoratedVariable; + + public get consumerVar2(): number { + return this.__backing_consumerVar2!.get(); + } + + public set consumerVar2(value: number) { + this.__backing_consumerVar2!.set(value); + } + + private __backing_consumerVar3?: IConsumerDecoratedVariable; + + public get consumerVar3(): boolean { + return this.__backing_consumerVar3!.get(); + } + + public set consumerVar3(value: boolean) { + this.__backing_consumerVar3!.set(value); + } + + private __backing_consumerVar4?: IConsumerDecoratedVariable; + + public get consumerVar4(): undefined { + return this.__backing_consumerVar4!.get(); + } + + public set consumerVar4(value: undefined) { + this.__backing_consumerVar4!.set(value); + } + + private __backing_consumerVar5?: IConsumerDecoratedVariable; + + public get consumerVar5(): null { + return this.__backing_consumerVar5!.get(); + } + + public set consumerVar5(value: null) { + this.__backing_consumerVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set consumerVar1(consumerVar1: (string | undefined)) + + get consumerVar1(): (string | undefined) + set __backing_consumerVar1(__backing_consumerVar1: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar1(): (IConsumerDecoratedVariable | undefined) + set consumerVar2(consumerVar2: (number | undefined)) + + get consumerVar2(): (number | undefined) + set __backing_consumerVar2(__backing_consumerVar2: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar2(): (IConsumerDecoratedVariable | undefined) + set consumerVar3(consumerVar3: (boolean | undefined)) + + get consumerVar3(): (boolean | undefined) + set __backing_consumerVar3(__backing_consumerVar3: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar3(): (IConsumerDecoratedVariable | undefined) + set consumerVar4(consumerVar4: (undefined | undefined)) + + get consumerVar4(): (undefined | undefined) + set __backing_consumerVar4(__backing_consumerVar4: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar4(): (IConsumerDecoratedVariable | undefined) + set consumerVar5(consumerVar5: (null | undefined)) + + get consumerVar5(): (null | undefined) + set __backing_consumerVar5(__backing_consumerVar5: (IConsumerDecoratedVariable | undefined)) + + get __backing_consumerVar5(): (IConsumerDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Consumer decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..809d7e3ceaa3ffb18937d406b9a528545549de57 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/consumer-complex-type.test.ts @@ -0,0 +1,351 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'consumer-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Consumer decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Consumer as Consumer } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar1", "paramVar1", new Per(6)); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar2", "paramVar2", new Array(3, 6, 8)); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar3", "paramVar3", StateType.TYPE3); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar4", "paramVar4", new Set(new Array("aa", "bb"))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar5", "paramVar5", [true, false]); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar6", "paramVar6", new Array(new Per(7), new Per(11))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar7", "paramVar7", [new Per(7), new Per(11)]); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeConsumer(this, "paramVar9", "paramVar9", new Date("2025-4-23")); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeConsumer>(this, "paramVar10", "paramVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeConsumer<(string | number)>(this, "paramVar11", "paramVar11", 0.0); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeConsumer<(Set | Per)>(this, "paramVar12", "paramVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_paramVar1?: IConsumerDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + public set paramVar1(value: Per) { + this.__backing_paramVar1!.set(value); + } + + private __backing_paramVar2?: IConsumerDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + public set paramVar2(value: Array) { + this.__backing_paramVar2!.set(value); + } + + private __backing_paramVar3?: IConsumerDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + public set paramVar3(value: StateType) { + this.__backing_paramVar3!.set(value); + } + + private __backing_paramVar4?: IConsumerDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + public set paramVar4(value: Set) { + this.__backing_paramVar4!.set(value); + } + + private __backing_paramVar5?: IConsumerDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + public set paramVar5(value: Array) { + this.__backing_paramVar5!.set(value); + } + + private __backing_paramVar6?: IConsumerDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + public set paramVar6(value: Array) { + this.__backing_paramVar6!.set(value); + } + + private __backing_paramVar7?: IConsumerDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + public set paramVar7(value: Array) { + this.__backing_paramVar7!.set(value); + } + + private __backing_paramVar9?: IConsumerDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + public set paramVar9(value: Date) { + this.__backing_paramVar9!.set(value); + } + + private __backing_paramVar10?: IConsumerDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + public set paramVar10(value: Map) { + this.__backing_paramVar10!.set(value); + } + + private __backing_paramVar11?: IConsumerDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + public set paramVar11(value: (string | number)) { + this.__backing_paramVar11!.set(value); + } + + private __backing_paramVar12?: IConsumerDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + public set paramVar12(value: (Set | Per)) { + this.__backing_paramVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IConsumerDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IConsumerDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IConsumerDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IConsumerDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IConsumerDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IConsumerDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IConsumerDecoratedVariable> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IConsumerDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IConsumerDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IConsumerDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IConsumerDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IConsumerDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IConsumerDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IConsumerDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IConsumerDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Consumer decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9943acaee83f7f67d0c7fb53e4f25bcbf98c386c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-basic-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Provider decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Provider as Provider } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_providerVar1 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar1", "providerVar1", "propVar1"); + this.__backing_providerVar2 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar2", "providerVar2", 50); + this.__backing_providerVar3 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar3", "providerVar3", true); + this.__backing_providerVar4 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar4", "providerVar4", undefined); + this.__backing_providerVar5 = STATE_MGMT_FACTORY.makeProvider(this, "providerVar5", "providerVar5", null); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_providerVar1?: IProviderDecoratedVariable; + + public get providerVar1(): string { + return this.__backing_providerVar1!.get(); + } + + public set providerVar1(value: string) { + this.__backing_providerVar1!.set(value); + } + + private __backing_providerVar2?: IProviderDecoratedVariable; + + public get providerVar2(): number { + return this.__backing_providerVar2!.get(); + } + + public set providerVar2(value: number) { + this.__backing_providerVar2!.set(value); + } + + private __backing_providerVar3?: IProviderDecoratedVariable; + + public get providerVar3(): boolean { + return this.__backing_providerVar3!.get(); + } + + public set providerVar3(value: boolean) { + this.__backing_providerVar3!.set(value); + } + + private __backing_providerVar4?: IProviderDecoratedVariable; + + public get providerVar4(): undefined { + return this.__backing_providerVar4!.get(); + } + + public set providerVar4(value: undefined) { + this.__backing_providerVar4!.set(value); + } + + private __backing_providerVar5?: IProviderDecoratedVariable; + + public get providerVar5(): null { + return this.__backing_providerVar5!.get(); + } + + public set providerVar5(value: null) { + this.__backing_providerVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set providerVar1(providerVar1: (string | undefined)) + + get providerVar1(): (string | undefined) + set __backing_providerVar1(__backing_providerVar1: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar1(): (IProviderDecoratedVariable | undefined) + set providerVar2(providerVar2: (number | undefined)) + + get providerVar2(): (number | undefined) + set __backing_providerVar2(__backing_providerVar2: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar2(): (IProviderDecoratedVariable | undefined) + set providerVar3(providerVar3: (boolean | undefined)) + + get providerVar3(): (boolean | undefined) + set __backing_providerVar3(__backing_providerVar3: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar3(): (IProviderDecoratedVariable | undefined) + set providerVar4(providerVar4: (undefined | undefined)) + + get providerVar4(): (undefined | undefined) + set __backing_providerVar4(__backing_providerVar4: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar4(): (IProviderDecoratedVariable | undefined) + set providerVar5(providerVar5: (null | undefined)) + + get providerVar5(): (null | undefined) + set __backing_providerVar5(__backing_providerVar5: (IProviderDecoratedVariable | undefined)) + + get __backing_providerVar5(): (IProviderDecoratedVariable | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test basic type @Provider decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a953691a71a49446d0df769073081cbee304b71 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-complex-type.test.ts @@ -0,0 +1,351 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Provider decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2 } from "@ohos.arkui.component"; + +import { Provider as Provider } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class StateType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: StateType = new StateType(0, 0); + + public static readonly TYPE2: StateType = new StateType(1, 1); + + public static readonly TYPE3: StateType = new StateType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + + public getName(): String { + return StateType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): StateType { + for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { + if (((name) == (StateType.#NamesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant StateType.") + (name))); + } + + public static fromValue(value: int): StateType { + for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { + if (((value) == (StateType.#ValuesArray[i]))) { + return StateType.#ItemsArray[i]; + } + } + throw new Error((("No enum StateType with value ") + (value))); + } + + public valueOf(): int { + return StateType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return StateType.#StringValuesArray[this.#ordinal]; + } + + public static values(): StateType[] { + return StateType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: StateType): String { + return e.getName(); + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_paramVar1 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar1", "paramVar1", new Per(6)); + this.__backing_paramVar2 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar2", "paramVar2", new Array(3, 6, 8)); + this.__backing_paramVar3 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar3", "paramVar3", StateType.TYPE3); + this.__backing_paramVar4 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar4", "paramVar4", new Set(new Array("aa", "bb"))); + this.__backing_paramVar5 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar5", "paramVar5", [true, false]); + this.__backing_paramVar6 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar6", "paramVar6", new Array(new Per(7), new Per(11))); + this.__backing_paramVar7 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar7", "paramVar7", [new Per(7), new Per(11)]); + this.__backing_paramVar9 = STATE_MGMT_FACTORY.makeProvider(this, "paramVar9", "paramVar9", new Date("2025-4-23")); + this.__backing_paramVar10 = STATE_MGMT_FACTORY.makeProvider>(this, "paramVar10", "paramVar10", new Map([[0, new Per(7)], [1, new Per(10)]])); + this.__backing_paramVar11 = STATE_MGMT_FACTORY.makeProvider<(string | number)>(this, "paramVar11", "paramVar11", 0.0); + this.__backing_paramVar12 = STATE_MGMT_FACTORY.makeProvider<(Set | Per)>(this, "paramVar12", "paramVar12", new Per(6)); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_paramVar1?: IProviderDecoratedVariable; + + public get paramVar1(): Per { + return this.__backing_paramVar1!.get(); + } + + public set paramVar1(value: Per) { + this.__backing_paramVar1!.set(value); + } + + private __backing_paramVar2?: IProviderDecoratedVariable>; + + public get paramVar2(): Array { + return this.__backing_paramVar2!.get(); + } + + public set paramVar2(value: Array) { + this.__backing_paramVar2!.set(value); + } + + private __backing_paramVar3?: IProviderDecoratedVariable; + + public get paramVar3(): StateType { + return this.__backing_paramVar3!.get(); + } + + public set paramVar3(value: StateType) { + this.__backing_paramVar3!.set(value); + } + + private __backing_paramVar4?: IProviderDecoratedVariable>; + + public get paramVar4(): Set { + return this.__backing_paramVar4!.get(); + } + + public set paramVar4(value: Set) { + this.__backing_paramVar4!.set(value); + } + + private __backing_paramVar5?: IProviderDecoratedVariable>; + + public get paramVar5(): Array { + return this.__backing_paramVar5!.get(); + } + + public set paramVar5(value: Array) { + this.__backing_paramVar5!.set(value); + } + + private __backing_paramVar6?: IProviderDecoratedVariable>; + + public get paramVar6(): Array { + return this.__backing_paramVar6!.get(); + } + + public set paramVar6(value: Array) { + this.__backing_paramVar6!.set(value); + } + + private __backing_paramVar7?: IProviderDecoratedVariable>; + + public get paramVar7(): Array { + return this.__backing_paramVar7!.get(); + } + + public set paramVar7(value: Array) { + this.__backing_paramVar7!.set(value); + } + + private __backing_paramVar9?: IProviderDecoratedVariable; + + public get paramVar9(): Date { + return this.__backing_paramVar9!.get(); + } + + public set paramVar9(value: Date) { + this.__backing_paramVar9!.set(value); + } + + private __backing_paramVar10?: IProviderDecoratedVariable>; + + public get paramVar10(): Map { + return this.__backing_paramVar10!.get(); + } + + public set paramVar10(value: Map) { + this.__backing_paramVar10!.set(value); + } + + private __backing_paramVar11?: IProviderDecoratedVariable<(string | number)>; + + public get paramVar11(): (string | number) { + return this.__backing_paramVar11!.get(); + } + + public set paramVar11(value: (string | number)) { + this.__backing_paramVar11!.set(value); + } + + private __backing_paramVar12?: IProviderDecoratedVariable<(Set | Per)>; + + public get paramVar12(): (Set | Per) { + return this.__backing_paramVar12!.get(); + } + + public set paramVar12(value: (Set | Per)) { + this.__backing_paramVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set paramVar1(paramVar1: (Per | undefined)) + + get paramVar1(): (Per | undefined) + set __backing_paramVar1(__backing_paramVar1: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar1(): (IProviderDecoratedVariable | undefined) + set paramVar2(paramVar2: (Array | undefined)) + + get paramVar2(): (Array | undefined) + set __backing_paramVar2(__backing_paramVar2: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar2(): (IProviderDecoratedVariable> | undefined) + set paramVar3(paramVar3: (StateType | undefined)) + + get paramVar3(): (StateType | undefined) + set __backing_paramVar3(__backing_paramVar3: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar3(): (IProviderDecoratedVariable | undefined) + set paramVar4(paramVar4: (Set | undefined)) + + get paramVar4(): (Set | undefined) + set __backing_paramVar4(__backing_paramVar4: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar4(): (IProviderDecoratedVariable> | undefined) + set paramVar5(paramVar5: (Array | undefined)) + + get paramVar5(): (Array | undefined) + set __backing_paramVar5(__backing_paramVar5: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar5(): (IProviderDecoratedVariable> | undefined) + set paramVar6(paramVar6: (Array | undefined)) + + get paramVar6(): (Array | undefined) + set __backing_paramVar6(__backing_paramVar6: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar6(): (IProviderDecoratedVariable> | undefined) + set paramVar7(paramVar7: (Array | undefined)) + + get paramVar7(): (Array | undefined) + set __backing_paramVar7(__backing_paramVar7: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar7(): (IProviderDecoratedVariable> | undefined) + set paramVar9(paramVar9: (Date | undefined)) + + get paramVar9(): (Date | undefined) + set __backing_paramVar9(__backing_paramVar9: (IProviderDecoratedVariable | undefined)) + + get __backing_paramVar9(): (IProviderDecoratedVariable | undefined) + set paramVar10(paramVar10: (Map | undefined)) + + get paramVar10(): (Map | undefined) + set __backing_paramVar10(__backing_paramVar10: (IProviderDecoratedVariable> | undefined)) + + get __backing_paramVar10(): (IProviderDecoratedVariable> | undefined) + set paramVar11(paramVar11: ((string | number) | undefined)) + + get paramVar11(): ((string | number) | undefined) + set __backing_paramVar11(__backing_paramVar11: (IProviderDecoratedVariable<(string | number)> | undefined)) + + get __backing_paramVar11(): (IProviderDecoratedVariable<(string | number)> | undefined) + set paramVar12(paramVar12: ((Set | Per) | undefined)) + + get paramVar12(): ((Set | Per) | undefined) + set __backing_paramVar12(__backing_paramVar12: (IProviderDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_paramVar12(): (IProviderDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test complex type @Provider decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..89a5a701f9eb7a019ef1711eee2b086039e73c25 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provider-and-consumer/provider-to-consumer.test.ts @@ -0,0 +1,261 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/provider-and-consumer'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'provider-to-consumer.ets'), +]; + +const pluginTester = new PluginTester('test usage of @Provider and @Consumer decorator', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedCheckedScript: string = ` +import { IConsumerDecoratedVariable as IConsumerDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { IProviderDecoratedVariable as IProviderDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { UIUtils as UIUtils } from "arkui.stateManagement.utils"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { ComponentV2 as ComponentV2, DragEvent as DragEvent, Button as Button, Column as Column, Text as Text, ForEach as ForEach, Divider as Divider } from "@ohos.arkui.component"; + +import { Provider as Provider, Consumer as Consumer, Local as Local, ObservedV2 as ObservedV2, Trace as Trace } from "@ohos.arkui.stateManagement"; + +const data: Array = [new User("Json", 10), new User("Eric", 15)]; + +function main() {} + +data = [new User("Json", 10), new User("Eric", 15)]; + +@ObservedV2() class User implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + public setV1RenderId(renderId: RenderIdType): void {} + + protected conditionalAddRef(meta: IMutableStateMeta): void { + meta.addRef(); + } + + @JSONRename({newName:"name"}) private __backing_name?: string; + + @JSONStringifyIgnore() private __meta_name: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"age"}) private __backing_age?: number; + + @JSONStringifyIgnore() private __meta_age: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public get name(): string { + this.conditionalAddRef(this.__meta_name); + return UIUtils.makeObserved((this.__backing_name as string)); + } + + public set name(newValue: string) { + if (((this.__backing_name) !== (newValue))) { + this.__backing_name = newValue; + this.__meta_name.fireChange(); + this.executeOnSubscribingWatches("name"); + } + } + + public get age(): number { + this.conditionalAddRef(this.__meta_age); + return UIUtils.makeObserved((this.__backing_age as number)); + } + + public set age(newValue: number) { + if (((this.__backing_age) !== (newValue))) { + this.__backing_age = newValue; + this.__meta_age.fireChange(); + this.executeOnSubscribingWatches("age"); + } + } + + public constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + +} + +@ComponentV2() final struct Parent extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_users = STATE_MGMT_FACTORY.makeProvider>(this, "users", "data", data); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_users?: IProviderDecoratedVariable>; + + public get users(): Array { + return this.__backing_users!.get(); + } + + public set users(value: Array) { + this.__backing_users!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.users.push(new User("Molly", 18)); + })); + return; + }), "add new user", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + (this.users[0].age++); + })); + return; + }), "age++", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e) => { + this.users[0].name = "Shelly"; + })); + return; + }), "change name", undefined, undefined); + })); + } + + public constructor() {} + +} + +@ComponentV2() final struct Child extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_users = STATE_MGMT_FACTORY.makeConsumer>(this, "users", "data", []); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_users?: IConsumerDecoratedVariable>; + + public get users(): Array { + return this.__backing_users!.get(); + } + + public set users(value: Array) { + this.__backing_users!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + ForEach(((): Array => { + return this.users; + }), ((item: User) => { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), \`name: \${item.name}\`, undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), \`age: \${item.age}\`, undefined, undefined); + Divider(undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@ComponentV2() export interface __Options_Parent { + set users(users: (Array | undefined)) + + get users(): (Array | undefined) + set __backing_users(__backing_users: (IProviderDecoratedVariable> | undefined)) + + get __backing_users(): (IProviderDecoratedVariable> | undefined) + +} + +@ComponentV2() export interface __Options_Child { + set users(users: (Array | undefined)) + + get users(): (Array | undefined) + set __backing_users(__backing_users: (IConsumerDecoratedVariable> | undefined)) + + get __backing_users(): (IConsumerDecoratedVariable> | undefined) + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test usage of @Provider and @Consumer decorator', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7892cf9f6c32f8652f69f650cce4c6091ccd0ea --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts @@ -0,0 +1,354 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STATE_DIR_PATH: string = 'decorators/require'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'basic-require.ets'), +]; + +const pluginTester = new PluginTester('test @Require decorator capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedParsedScript: string = ` +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; + +@Component() final struct MyStateSample extends CustomComponent { + public hello: string = "hello"; + @State() public state1: boolean = false; + @Require() public select100!: string; + @Require() @State() public select0!: number; + @Require() @State() public select3?: (number | null); + @Require() @State() public select4?: undefined; + @Require() @Prop() public select1!: string; + @Require() @Provide({alias:"15"}) public select2!: string[]; + @Require() @Provide({alias:"t"}) public select6?: (string[] | undefined | string); + @Require() @BuilderParam() public builder!: (()=> void); + + public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2222 extends CustomComponentV2 { + @Require() @Param() public select1!: string; + + public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + hello?: string; + state1?: boolean; + @State() __backing_state1?: boolean; + select100?: string; + select0?: number; + @Require() @State() __backing_select0?: number; + select3?: (number | null); + @Require() @State() __backing_select3?: (number | null); + select4?: undefined; + @Require() @State() __backing_select4?: undefined; + select1?: string; + @Require() @Prop() __backing_select1?: string; + select2?: string[]; + @Require() @Provide({alias:"15"}) __backing_select2?: string[]; + select6?: (string[] | undefined | string); + @Require() @Provide({alias:"t"}) __backing_select6?: (string[] | undefined | string); + @BuilderParam() builder?: (()=> void); + +} + +@ComponentV2() export interface __Options_V2222 { + select1?: string; + @Require() @Param() __backing_select1?: string; + +} +`; + +const expectedCheckedScript: string = ` +import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_hello = ((({let gensym___159351621 = initializers; + (((gensym___159351621) == (null)) ? undefined : gensym___159351621.hello)})) ?? ("hello")); + this.__backing_state1 = STATE_MGMT_FACTORY.makeState(this, "state1", ((({let gensym___152317197 = initializers; + (((gensym___152317197) == (null)) ? undefined : gensym___152317197.state1)})) ?? (false))); + this.__backing_select100 = ((({let gensym___257385749 = initializers; + (((gensym___257385749) == (null)) ? undefined : gensym___257385749.select100)})) ?? (undefined)); + this.__backing_select0 = STATE_MGMT_FACTORY.makeState(this, "select0", (initializers!.select0 as number)); + this.__backing_select3 = STATE_MGMT_FACTORY.makeState<(number | null)>(this, "select3", (initializers!.select3 as (number | null))); + this.__backing_select4 = STATE_MGMT_FACTORY.makeState(this, "select4", (initializers!.select4 as undefined)); + this.__backing_select1 = STATE_MGMT_FACTORY.makeProp(this, "select1", (initializers!.select1 as string)); + this.__backing_select2 = STATE_MGMT_FACTORY.makeProvide>(this, "select2", "15", (initializers!.select2 as Array), false); + this.__backing_select6 = STATE_MGMT_FACTORY.makeProvide<(Array | undefined | string)>(this, "select6", "t", (initializers!.select6 as (Array | undefined | string)), false); + this.__backing_builder = ((((({let gensym___57081607 = initializers; + (((gensym___57081607) == (null)) ? undefined : gensym___57081607.builder)})) ?? (content))) ?? (undefined)) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { + if (((({let gensym___171969630 = initializers; + (((gensym___171969630) == (null)) ? undefined : gensym___171969630.select1)})) !== (undefined))) { + this.__backing_select1!.update((initializers!.select1 as string)); + } + } + + private __backing_hello?: string; + + public get hello(): string { + return (this.__backing_hello as string); + } + + public set hello(value: string) { + this.__backing_hello = value; + } + + private __backing_state1?: IStateDecoratedVariable; + + public get state1(): boolean { + return this.__backing_state1!.get(); + } + + public set state1(value: boolean) { + this.__backing_state1!.set(value); + } + + private __backing_select100?: string; + + public get select100(): string { + return (this.__backing_select100 as string); + } + + public set select100(value: string) { + this.__backing_select100 = value; + } + + private __backing_select0?: IStateDecoratedVariable; + + public get select0(): number { + return this.__backing_select0!.get(); + } + + public set select0(value: number) { + this.__backing_select0!.set(value); + } + + private __backing_select3?: IStateDecoratedVariable<(number | null)>; + + public get select3(): (number | null) { + return this.__backing_select3!.get(); + } + + public set select3(value: (number | null)) { + this.__backing_select3!.set(value); + } + + private __backing_select4?: IStateDecoratedVariable; + + public get select4(): undefined { + return this.__backing_select4!.get(); + } + + public set select4(value: undefined) { + this.__backing_select4!.set(value); + } + + private __backing_select1?: IPropDecoratedVariable; + + public get select1(): string { + return this.__backing_select1!.get(); + } + + public set select1(value: string) { + this.__backing_select1!.set(value); + } + + private __backing_select2?: IProvideDecoratedVariable>; + + public get select2(): Array { + return this.__backing_select2!.get(); + } + + public set select2(value: Array) { + this.__backing_select2!.set(value); + } + + private __backing_select6?: IProvideDecoratedVariable<(Array | undefined | string)>; + + public get select6(): (Array | undefined | string) { + return this.__backing_select6!.get(); + } + + public set select6(value: (Array | undefined | string)) { + this.__backing_select6!.set(value); + } + + private __backing_builder?: @memo() (()=> void); + + public get builder(): @memo() (()=> void) { + return this.__backing_builder!; + } + + public set builder(value: @memo() (()=> void)) { + this.__backing_builder = value; + } + + @memo() public build() {} + + public constructor() {} + +} + +@ComponentV2() final struct V2222 extends CustomComponentV2 { + public __initializeStruct(initializers: (__Options_V2222 | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_select1 = STATE_MGMT_FACTORY.makeParam(this, "select1", (initializers!.select1 as string)); + } + + public __updateStruct(initializers: (__Options_V2222 | undefined)): void { + if (((({let gensym___155019449 = initializers; + (((gensym___155019449) == (null)) ? undefined : gensym___155019449.select1)})) !== (undefined))) { + this.__backing_select1!.update((initializers!.select1 as string)); + } + } + + private __backing_select1?: IParamDecoratedVariable; + + public get select1(): string { + return this.__backing_select1!.get(); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set hello(hello: (string | undefined)) + + get hello(): (string | undefined) + set state1(state1: (boolean | undefined)) + + get state1(): (boolean | undefined) + set __backing_state1(__backing_state1: (IStateDecoratedVariable | undefined)) + + get __backing_state1(): (IStateDecoratedVariable | undefined) + set select100(select100: (string | undefined)) + + get select100(): (string | undefined) + set select0(select0: (number | undefined)) + + get select0(): (number | undefined) + @Require() set __backing_select0(__backing_select0: (IStateDecoratedVariable | undefined)) + + @Require() get __backing_select0(): (IStateDecoratedVariable | undefined) + set select3(select3: ((number | null) | undefined)) + + get select3(): ((number | null) | undefined) + @Require() set __backing_select3(__backing_select3: (IStateDecoratedVariable<(number | null)> | undefined)) + + @Require() get __backing_select3(): (IStateDecoratedVariable<(number | null)> | undefined) + set select4(select4: (undefined | undefined)) + + get select4(): (undefined | undefined) + @Require() set __backing_select4(__backing_select4: (IStateDecoratedVariable | undefined)) + + @Require() get __backing_select4(): (IStateDecoratedVariable | undefined) + set select1(select1: (string | undefined)) + + get select1(): (string | undefined) + @Require() set __backing_select1(__backing_select1: (IPropDecoratedVariable | undefined)) + + @Require() get __backing_select1(): (IPropDecoratedVariable | undefined) + set select2(select2: (Array | undefined)) + + get select2(): (Array | undefined) + @Require() set __backing_select2(__backing_select2: (IProvideDecoratedVariable> | undefined)) + + @Require() get __backing_select2(): (IProvideDecoratedVariable> | undefined) + set select6(select6: ((Array | undefined | string) | undefined)) + + get select6(): ((Array | undefined | string) | undefined) + @Require() set __backing_select6(__backing_select6: (IProvideDecoratedVariable<(Array | undefined | string)> | undefined)) + + @Require() get __backing_select6(): (IProvideDecoratedVariable<(Array | undefined | string)> | undefined) + set builder(builder: (@memo() (()=> void) | undefined)) + + get builder(): (@memo() (()=> void) | undefined) + +} + +@ComponentV2() export interface __Options_V2222 { + set select1(select1: (string | undefined)) + + get select1(): (string | undefined) + @Require() set __backing_select1(__backing_select1: (IParamDecoratedVariable | undefined)) + + @Require() get __backing_select1(): (IParamDecoratedVariable | undefined) + +} +`; + +function testParsedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test @Require decorator capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testParsedTransformer], + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts index ad9e6798bfc7ccefd79c5de649aa8965ae5e6969..a0e46261746cfc5dd43c78990fe2785daf27d699 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -33,96 +34,128 @@ const pluginTester = new PluginTester('test resource transform in build method', const parsedTransform: Plugins = { name: 'resource-in-build', - parsed: uiTransform().parsed + parsed: uiTransform().parsed, }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { _rawfile as _rawfile } from "arkui.component.resources"; + +import { ImageAnimatorAttribute as ImageAnimatorAttribute } from "arkui.component.imageAnimator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIImageAnimatorAttribute as UIImageAnimatorAttribute } from "@ohos.arkui.component"; -import { UISelectAttribute as UISelectAttribute } from "@ohos.arkui.component"; -import { UITextInputAttribute as UITextInputAttribute } from "@ohos.arkui.component"; -import { UIImageAttribute as UIImageAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { _rawfile as _rawfile } from "@ohos.arkui.component"; -import { _r as _r } from "@ohos.arkui.component"; + +import { ImageAttribute as ImageAttribute } from "arkui.component.image"; + +import { _r as _r } from "arkui.component.resources"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, TextInput as TextInput, Select as Select, SelectOption as SelectOption, Margin as Margin, ImageAnimator as ImageAnimator } from "@ohos.arkui.component"; + +import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, TextInput as TextInput, Select as Select, SelectOption as SelectOption, Margin as Margin, ImageAnimator as ImageAnimator, Resource as Resource } from "@ohos.arkui.component"; function main() {} -@Component({freezeWhenInactive:false}) final class ResourceComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ResourceComponent | undefined, @memo() content: (()=> void) | undefined): void { +@Component() final struct ResourceComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ResourceComponent | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_str1 = ((({let gensym___147578113 = initializers; (((gensym___147578113) == (null)) ? undefined : gensym___147578113.str1)})) ?? ("app.media.ri")); this.__backing_str2 = ((({let gensym___220149772 = initializers; (((gensym___220149772) == (null)) ? undefined : gensym___220149772.str2)})) ?? ("app.photo2.png")); + this.__backing_numbers = ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.numbers)})) ?? (["0", "1", "3", "5", "8"])); } - public __updateStruct(initializers: __Options_ResourceComponent | undefined): void {} + + public __updateStruct(initializers: (__Options_ResourceComponent | undefined)): void {} + private __backing_str1?: string; + public get str1(): string { return (this.__backing_str1 as string); } + public set str1(value: string) { this.__backing_str1 = value; } + private __backing_str2?: string; + public get str2(): string { return (this.__backing_str2 as string); } + public set str2(value: string) { this.__backing_str2 = value; } + private __backing_numbers?: Array; + + public get numbers(): Array { + return (this.__backing_numbers as Array); + } + + public set numbers(value: Array) { + this.__backing_numbers = value; + } + public aboutToAppear() { + let arr: Array = new Array(); + for (let i = 0;((i) < (5));(i++)) { + arr.push(_r(16777216, 10003, "com.example.mock", "entry")); + } + for (let item of this.numbers) { + arr.push(_r(16777216, 10003, "com.example.mock", "entry")); + } + } - @memo() public _build(@memo() style: ((instance: ResourceComponent)=> ResourceComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ResourceComponent | undefined): void { - Column(undefined, undefined, (() => { - Text(undefined, _r("", "", "app.string.app_name"), undefined, undefined); - Image(undefined, _rawfile("", "", "app.photo.png"), undefined, undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, _r(16777216, 10003, "com.example.mock", "entry"), undefined, undefined); + Image(undefined, _rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"), undefined, undefined); TextInput(undefined, { - text: _r("", "", "app.string.input_content"), + text: _r(16777220, 10003, "com.example.mock", "entry"), }, undefined); - Text(undefined, _r("", "", this.str1), undefined, undefined); - Text(undefined, _r("", "", this.str2), undefined, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str1), undefined, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str2), undefined, undefined); Select(undefined, new Array({ value: "aaa", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "bbb", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "ccc", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }, { value: "ddd", - icon: _r("", "", "app.media.selection"), + icon: _r(16777223, 20000, "com.example.mock", "entry"), }), undefined); - Image(@memo() ((instance: UIImageAttribute): void => { + Image(@memo() ((instance: ImageAttribute): void => { instance.margin(({ - top: _r("", "", "app.float.elements_margin_horizontal_m"), - bottom: _r("", "", "app.float.elements_margin_horizontal_l"), + top: _r(16777222, 10002, "com.example.mock", "entry"), + bottom: _r(16777222, 10002, "com.example.mock", "entry"), } as Margin)); return; - }), _r("", "", "app.media.app_icon"), undefined, undefined); - ImageAnimator(@memo() ((instance: UIImageAnimatorAttribute): void => { + }), _r(16777217, 20000, "com.example.mock", "entry"), undefined, undefined); + ImageAnimator(@memo() ((instance: ImageAnimatorAttribute): void => { instance.images([{ - src: _r("", "", "app.media.aaa"), + src: _r(16777217, 20000, "com.example.mock", "entry"), }, { - src: _r("", "", "app.media.bbb"), + src: _r(16777225, 20000, "com.example.mock", "entry"), }]); return; - }), undefined); + })); })); } + public constructor() {} + } -interface __Options_ResourceComponent { - set str1(str1: string | undefined) - get str1(): string | undefined - set str2(str2: string | undefined) - get str2(): string | undefined +@Component() export interface __Options_ResourceComponent { + set str1(str1: (string | undefined)) + get str1(): (string | undefined) + set str2(str2: (string | undefined)) + get str2(): (string | undefined) + set numbers(numbers: (Array | undefined)) + get numbers(): (Array | undefined) } `; @@ -132,9 +165,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test resource transform in build method', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts index 741751b9f990049cc0999c3afe3bfdbb0fb74d61..8856ce40285a8d5f668d83a9c6439a564614d1e4 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,29 +38,25 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { UIImageAttribute as UIImageAttribute } from "@ohos.arkui.component"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; -import { _rawfile as _rawfile } from "@ohos.arkui.component"; -import { _r as _r } from "@ohos.arkui.component"; +import { _rawfile as _rawfile } from "arkui.component.resources"; +import { _r as _r } from "arkui.component.resources"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, Resource as Resource } from "@ohos.arkui.component"; let i: Resource; + function main() {} -i = _r("", "", "app.string.app_name"); -@Component({freezeWhenInactive:false}) final class ResourceComponent extends CustomComponent { - public __initializeStruct(initializers: __Options_ResourceComponent | undefined, @memo() content: (()=> void) | undefined): void { +i = _r(16777216, 10003, "com.example.mock", "entry"); +@Component() final struct ResourceComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ResourceComponent | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_str = ((({let gensym___42103502 = initializers; - (((gensym___42103502) == (null)) ? undefined : gensym___42103502.str)})) ?? (_r("", "", "app.string.app_name"))); + (((gensym___42103502) == (null)) ? undefined : gensym___42103502.str)})) ?? (_r(16777216, 10003, "com.example.mock", "entry"))); this.__backing_icon = ((({let gensym___38135554 = initializers; - (((gensym___38135554) == (null)) ? undefined : gensym___38135554.icon)})) ?? (_rawfile("", "", "app.photo.png"))); + (((gensym___38135554) == (null)) ? undefined : gensym___38135554.icon)})) ?? (_rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"))); } - public __updateStruct(initializers: __Options_ResourceComponent | undefined): void {} + public __updateStruct(initializers: (__Options_ResourceComponent | undefined)): void {} private __backing_str?: Resource; public get str(): Resource { return (this.__backing_str as Resource); @@ -74,21 +71,23 @@ i = _r("", "", "app.string.app_name"); public set icon(value: Resource) { this.__backing_icon = value; } - @memo() public _build(@memo() style: ((instance: ResourceComponent)=> ResourceComponent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_ResourceComponent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Text(undefined, this.str, undefined, undefined); Text(undefined, i, undefined, undefined); Image(undefined, this.icon, undefined, undefined); })); } public constructor() {} + } -interface __Options_ResourceComponent { - set str(str: Resource | undefined) - get str(): Resource | undefined - set icon(icon: Resource | undefined) - get icon(): Resource | undefined +@Component() export interface __Options_ResourceComponent { + set str(str: (Resource | undefined)) + get str(): (Resource | undefined) + set icon(icon: (Resource | undefined)) + get icon(): (Resource | undefined) } `; @@ -98,9 +97,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test resource transform in property', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts index 0589b25abf0770ba560c8f64696ec51e428e9836..1414b3d22ba530e1e74cd4b4f29d6e62fb120e87 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,58 +38,58 @@ const reusableTransform: Plugins = { const pluginTester = new PluginTester('test basic reusable', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Reusable as Reusable } from "@ohos.arkui.component"; -import { State as State, Link as Link } from "@ohos.arkui.stateManagement"; +import { State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void { + @memo() public build() { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { num: 5, - } as __Options_Child), undefined, "Child"); + }, "Child", undefined); } public constructor() {} } -@Component({freezeWhenInactive:false}) @Reusable() final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - if (({let gensym___98468840 = initializers; - (((gensym___98468840) == (null)) ? undefined : gensym___98468840.__backing_num)})) { - this.__backing_num = new LinkDecoratedVariable("num", initializers!.__backing_num!); - }; - this.__backing_num1 = new StateDecoratedVariable("num1", ((({let gensym___33833641 = initializers; - (((gensym___33833641) == (null)) ? undefined : gensym___33833641.num1)})) ?? (2))); +@Component() @Reusable() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeProp(this, "num", ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (1))); + this.__backing_num1 = STATE_MGMT_FACTORY.makeState(this, "num1", ((({let gensym___24398512 = initializers; + (((gensym___24398512) == (null)) ? undefined : gensym___24398512.num1)})) ?? (2))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___108716469 = initializers; + (((gensym___108716469) == (null)) ? undefined : gensym___108716469.num)})) !== (undefined))) { + this.__backing_num!.update((initializers!.num as number)); + } + } - public override __toRecord(params: Object): Record { + public override constructor __toRecord(params: Object): Record { const paramsCasted = (params as __Options_Child); return { "num": ((paramsCasted.num) ?? (new Object())), @@ -96,7 +97,7 @@ function main() {} }; } - private __backing_num?: LinkDecoratedVariable; + private __backing_num?: IPropDecoratedVariable; public get num(): number { return this.__backing_num!.get(); @@ -106,7 +107,7 @@ function main() {} this.__backing_num!.set(value); } - private __backing_num1?: StateDecoratedVariable; + private __backing_num1?: IStateDecoratedVariable; public get num1(): number { return this.__backing_num1!.get(); @@ -116,29 +117,29 @@ function main() {} this.__backing_num1!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { +@Component() export interface __Options_MyStateSample { } -interface __Options_Child { - set num(num: number | undefined) +@Component() @Reusable() export interface __Options_Child { + set num(num: (number | undefined)) - get num(): number | undefined - set __backing_num(__backing_num: DecoratedV1VariableBase | undefined) + get num(): (number | undefined) + set __backing_num(__backing_num: (IPropDecoratedVariable | undefined)) - get __backing_num(): DecoratedV1VariableBase | undefined - set num1(num1: number | undefined) + get __backing_num(): (IPropDecoratedVariable | undefined) + set num1(num1: (number | undefined)) - get num1(): number | undefined - set __backing_num1(__backing_num1: StateDecoratedVariable | undefined) + get num1(): (number | undefined) + set __backing_num1(__backing_num1: (IStateDecoratedVariable | undefined)) - get __backing_num1(): StateDecoratedVariable | undefined + get __backing_num1(): (IStateDecoratedVariable | undefined) } `; @@ -149,9 +150,9 @@ function testReusableTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic reusable', - [reusableTransform, uiNoRecheck], + [reusableTransform, uiNoRecheck, recheck], { - checked: [testReusableTransformer], + 'checked:ui-no-recheck': [testReusableTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts index 873738ced9f080079d9a4a5cb4e461ebb57f6f94..8c3c455dcfeb61496b5271feba853cb28a79e65c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,22 +38,30 @@ const reusableTransform: Plugins = { const pluginTester = new PluginTester('test complex reusable', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; -import { UIButtonAttribute as UIButtonAttribute } from "@ohos.arkui.component"; +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { memo as memo } from "arkui.stateManagement.runtime"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry, Reusable as Reusable, Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent, FontWeight as FontWeight } from "@ohos.arkui.component"; @@ -61,10 +70,16 @@ import { State as State } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/reusable/reusable-complex", + pageFullPath: "test/demo/mock/decorators/reusable/reusable-complex", + integratedHsp: "false", + } as NavInterface)); class Message { - public value: string | undefined; + public value: (string | undefined); public constructor(value: string) { this.value = value; @@ -72,15 +87,15 @@ class Message { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_display = new StateDecoratedVariable("display", ((({let gensym___83835842 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_display = STATE_MGMT_FACTORY.makeState(this, "display", ((({let gensym___83835842 = initializers; (((gensym___83835842) == (null)) ? undefined : gensym___83835842.display)})) ?? (true))); } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_display?: StateDecoratedVariable; + private __backing_display?: IStateDecoratedVariable; public get display(): boolean { return this.__backing_display!.get(); @@ -90,24 +105,28 @@ class Message { this.__backing_display!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(@memo() ((instance: UIColumnAttribute): void => { + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { instance.height("100%").width("100%"); return; - }), undefined, (() => { - Button(@memo() ((instance: UIButtonAttribute): void => { + }), undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.fontSize(30).fontWeight(FontWeight.Bold).onClick(((e: ClickEvent) => { this.display = !(this.display); })); return; }), "Hello", undefined, undefined); - if (this.display) { - Child._instantiateImpl(undefined, (() => { - return new Child(); - }), ({ - message: new Message("Child"), - } as __Options_Child), undefined, "Child"); - } + ConditionScope(@memo() (() => { + if (this.display) { + ConditionBranch(@memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + message: new Message("Child"), + }, "Child", undefined); + })); + } + })); })); } @@ -115,22 +134,22 @@ class Message { } -@Reusable() @Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_message = new StateDecoratedVariable("message", ((({let gensym___91869411 = initializers; +@Reusable() @Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, "message", ((({let gensym___91869411 = initializers; (((gensym___91869411) == (null)) ? undefined : gensym___91869411.message)})) ?? (new Message("AboutToReuse")))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void {} - public override __toRecord(params: Object): Record { + public override constructor __toRecord(params: Object): Record { const paramsCasted = (params as __Options_Child); return { "message": ((paramsCasted.message) ?? (new Object())), }; } - private __backing_message?: StateDecoratedVariable; + private __backing_message?: IStateDecoratedVariable; public get message(): Message { return this.__backing_message!.get(); @@ -144,12 +163,12 @@ class Message { console.info("Recycle ====Child=="); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { - Column(@memo() ((instance: UIColumnAttribute): void => { + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { instance.borderWidth(1).height(100); return; - }), undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + }), undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; }), this.message.value, undefined, undefined); @@ -160,23 +179,23 @@ class Message { } -interface __Options_Index { - set display(display: boolean | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set display(display: (boolean | undefined)) - get display(): boolean | undefined - set __backing_display(__backing_display: StateDecoratedVariable | undefined) + get display(): (boolean | undefined) + set __backing_display(__backing_display: (IStateDecoratedVariable | undefined)) - get __backing_display(): StateDecoratedVariable | undefined + get __backing_display(): (IStateDecoratedVariable | undefined) } -interface __Options_Child { - set message(message: Message | undefined) +@Reusable() @Component() export interface __Options_Child { + set message(message: (Message | undefined)) - get message(): Message | undefined - set __backing_message(__backing_message: StateDecoratedVariable | undefined) + get message(): (Message | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) - get __backing_message(): StateDecoratedVariable | undefined + get __backing_message(): (IStateDecoratedVariable | undefined) } @@ -190,7 +209,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testReusableTransformer(this: PluginTestContext): void { @@ -199,9 +217,9 @@ function testReusableTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex reusable', - [reusableTransform, uiNoRecheck], + [reusableTransform, uiNoRecheck, recheck], { - checked: [testReusableTransformer], + 'checked:ui-no-recheck': [testReusableTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts index 96d03ea4bf7ee85d2432a1a0864d36e4d7268084..b354198ae070d1e180cd0585b4ce56113dd6113d 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-basic-type.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-basic-type.ets'), ]; const pluginTester = new PluginTester('test basic type @State decorated variables transformation', buildConfig); @@ -37,91 +38,129 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_stateVar1 = new StateDecoratedVariable("stateVar1", ((({let gensym___213853607 = initializers; + + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", ((({let gensym___213853607 = initializers; (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? ("stateVar1"))); - this.__backing_stateVar2 = new StateDecoratedVariable("stateVar2", ((({let gensym___113574154 = initializers; + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeState(this, "stateVar2", ((({let gensym___113574154 = initializers; (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (50))); - this.__backing_stateVar3 = new StateDecoratedVariable("stateVar3", ((({let gensym___166994972 = initializers; + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (true))); - this.__backing_stateVar4 = new StateDecoratedVariable("stateVar4", ((({let gensym___148024261 = initializers; + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeState(this, "stateVar4", ((({let gensym___148024261 = initializers; (((gensym___148024261) == (null)) ? undefined : gensym___148024261.stateVar4)})) ?? (undefined))); - this.__backing_stateVar5 = new StateDecoratedVariable("stateVar5", ((({let gensym___99384342 = initializers; + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeState(this, "stateVar5", ((({let gensym___99384342 = initializers; (((gensym___99384342) == (null)) ? undefined : gensym___99384342.stateVar5)})) ?? (null))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_stateVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_stateVar1?: IStateDecoratedVariable; + public get stateVar1(): string { return this.__backing_stateVar1!.get(); } + public set stateVar1(value: string) { this.__backing_stateVar1!.set(value); } - private __backing_stateVar2?: StateDecoratedVariable; + + private __backing_stateVar2?: IStateDecoratedVariable; + public get stateVar2(): number { return this.__backing_stateVar2!.get(); } + public set stateVar2(value: number) { this.__backing_stateVar2!.set(value); } - private __backing_stateVar3?: StateDecoratedVariable; + + private __backing_stateVar3?: IStateDecoratedVariable; + public get stateVar3(): boolean { return this.__backing_stateVar3!.get(); } + public set stateVar3(value: boolean) { this.__backing_stateVar3!.set(value); } - private __backing_stateVar4?: StateDecoratedVariable; + + private __backing_stateVar4?: IStateDecoratedVariable; + public get stateVar4(): undefined { return this.__backing_stateVar4!.get(); } + public set stateVar4(value: undefined) { this.__backing_stateVar4!.set(value); } - private __backing_stateVar5?: StateDecoratedVariable; + + private __backing_stateVar5?: IStateDecoratedVariable; + public get stateVar5(): null { return this.__backing_stateVar5!.get(); } + public set stateVar5(value: null) { this.__backing_stateVar5!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set stateVar1(stateVar1: string | undefined) - get stateVar1(): string | undefined - set __backing_stateVar1(__backing_stateVar1: StateDecoratedVariable | undefined) - get __backing_stateVar1(): StateDecoratedVariable | undefined - set stateVar2(stateVar2: number | undefined) - get stateVar2(): number | undefined - set __backing_stateVar2(__backing_stateVar2: StateDecoratedVariable | undefined) - get __backing_stateVar2(): StateDecoratedVariable | undefined - set stateVar3(stateVar3: boolean | undefined) - get stateVar3(): boolean | undefined - set __backing_stateVar3(__backing_stateVar3: StateDecoratedVariable | undefined) - get __backing_stateVar3(): StateDecoratedVariable | undefined - set stateVar4(stateVar4: undefined | undefined) - get stateVar4(): undefined | undefined - set __backing_stateVar4(__backing_stateVar4: StateDecoratedVariable | undefined) - get __backing_stateVar4(): StateDecoratedVariable | undefined - set stateVar5(stateVar5: null | undefined) - get stateVar5(): null | undefined - set __backing_stateVar5(__backing_stateVar5: StateDecoratedVariable | undefined) - get __backing_stateVar5(): StateDecoratedVariable | undefined -} +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (string | undefined)) + + get stateVar1(): (string | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (number | undefined)) + + get stateVar2(): (number | undefined) + set __backing_stateVar2(__backing_stateVar2: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar2(): (IStateDecoratedVariable | undefined) + set stateVar3(stateVar3: (boolean | undefined)) + + get stateVar3(): (boolean | undefined) + set __backing_stateVar3(__backing_stateVar3: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IStateDecoratedVariable | undefined) + set stateVar4(stateVar4: (undefined | undefined)) + + get stateVar4(): (undefined | undefined) + set __backing_stateVar4(__backing_stateVar4: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar4(): (IStateDecoratedVariable | undefined) + set stateVar5(stateVar5: (null | undefined)) + + get stateVar5(): (null | undefined) + set __backing_stateVar5(__backing_stateVar5: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar5(): (IStateDecoratedVariable | undefined) + +} `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -130,9 +169,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic type @State decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts index e42e3400622c079e69f6b69147b05dad2c0483d6..40f1f71d93a8d5f551e308ecdef9da2bf22c4e5d 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { structNoRecheck } from '../../../../utils/plugins'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-complex-type.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-complex-type.ets'), ]; const pluginTester = new PluginTester('test complex type @State decorated variables transformation', buildConfig); @@ -37,40 +38,58 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} class Per { public num: number; + public constructor(num: number) { this.num = num; } + } final class StateType extends BaseEnum { private readonly #ordinal: int; + private static () {} + public constructor(ordinal: int, value: int) { super(value); this.#ordinal = ordinal; } + public static readonly TYPE1: StateType = new StateType(0, 0); + public static readonly TYPE2: StateType = new StateType(1, 1); + public static readonly TYPE3: StateType = new StateType(2, 3); + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + private static readonly #ValuesArray: int[] = [0, 1, 3]; + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + private static readonly #ItemsArray: StateType[] = [StateType.TYPE1, StateType.TYPE2, StateType.TYPE3]; + public getName(): String { return StateType.#NamesArray[this.#ordinal]; } + public static getValueOf(name: String): StateType { for (let i = 0;((i) < (StateType.#NamesArray.length));(++i)) { if (((name) == (StateType.#NamesArray[i]))) { @@ -79,6 +98,7 @@ final class StateType extends BaseEnum { } throw new Error((("No enum constant StateType.") + (name))); } + public static fromValue(value: int): StateType { for (let i = 0;((i) < (StateType.#ValuesArray.length));(++i)) { if (((value) == (StateType.#ValuesArray[i]))) { @@ -87,189 +107,261 @@ final class StateType extends BaseEnum { } throw new Error((("No enum StateType with value ") + (value))); } + public valueOf(): int { return StateType.#ValuesArray[this.#ordinal]; } + public toString(): String { return StateType.#StringValuesArray[this.#ordinal]; } + public static values(): StateType[] { return StateType.#ItemsArray; } + public getOrdinal(): int { return this.#ordinal; } + public static $_get(e: StateType): String { return e.getName(); } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_stateVar1 = new StateDecoratedVariable("stateVar1", ((({let gensym___213853607 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", ((({let gensym___213853607 = initializers; (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? (new Per(6)))); - this.__backing_stateVar2 = new StateDecoratedVariable>("stateVar2", ((({let gensym___113574154 = initializers; + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeState>(this, "stateVar2", ((({let gensym___113574154 = initializers; (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (new Array(3, 6, 8)))); - this.__backing_stateVar3 = new StateDecoratedVariable("stateVar3", ((({let gensym___166994972 = initializers; + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (StateType.TYPE3))); - this.__backing_stateVar4 = new StateDecoratedVariable>("stateVar4", ((({let gensym___148024261 = initializers; + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeState>(this, "stateVar4", ((({let gensym___148024261 = initializers; (((gensym___148024261) == (null)) ? undefined : gensym___148024261.stateVar4)})) ?? (new Set(new Array("aa", "bb"))))); - this.__backing_stateVar5 = new StateDecoratedVariable>("stateVar5", ((({let gensym___99384342 = initializers; + this.__backing_stateVar5 = STATE_MGMT_FACTORY.makeState>(this, "stateVar5", ((({let gensym___99384342 = initializers; (((gensym___99384342) == (null)) ? undefined : gensym___99384342.stateVar5)})) ?? ([true, false]))); - this.__backing_stateVar6 = new StateDecoratedVariable>("stateVar6", ((({let gensym___133364871 = initializers; + this.__backing_stateVar6 = STATE_MGMT_FACTORY.makeState>(this, "stateVar6", ((({let gensym___133364871 = initializers; (((gensym___133364871) == (null)) ? undefined : gensym___133364871.stateVar6)})) ?? (new Array(new Per(7), new Per(11))))); - this.__backing_stateVar7 = new StateDecoratedVariable>("stateVar7", ((({let gensym___69403028 = initializers; + this.__backing_stateVar7 = STATE_MGMT_FACTORY.makeState>(this, "stateVar7", ((({let gensym___69403028 = initializers; (((gensym___69403028) == (null)) ? undefined : gensym___69403028.stateVar7)})) ?? ([new Per(7), new Per(11)]))); - this.__backing_stateVar8 = new StateDecoratedVariable<((sr: string)=> void)>("stateVar8", ((({let gensym___219403122 = initializers; + this.__backing_stateVar8 = STATE_MGMT_FACTORY.makeState<((sr: string)=> void)>(this, "stateVar8", ((({let gensym___219403122 = initializers; (((gensym___219403122) == (null)) ? undefined : gensym___219403122.stateVar8)})) ?? (((sr: string) => {})))); - this.__backing_stateVar9 = new StateDecoratedVariable("stateVar9", ((({let gensym___171171899 = initializers; + this.__backing_stateVar9 = STATE_MGMT_FACTORY.makeState(this, "stateVar9", ((({let gensym___171171899 = initializers; (((gensym___171171899) == (null)) ? undefined : gensym___171171899.stateVar9)})) ?? (new Date("2025-4-23")))); - this.__backing_stateVar10 = new StateDecoratedVariable>("stateVar10", ((({let gensym___91651348 = initializers; + this.__backing_stateVar10 = STATE_MGMT_FACTORY.makeState>(this, "stateVar10", ((({let gensym___91651348 = initializers; (((gensym___91651348) == (null)) ? undefined : gensym___91651348.stateVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); - this.__backing_stateVar11 = new StateDecoratedVariable("stateVar11", ((({let gensym___56045278 = initializers; + this.__backing_stateVar11 = STATE_MGMT_FACTORY.makeState<(string | number)>(this, "stateVar11", ((({let gensym___56045278 = initializers; (((gensym___56045278) == (null)) ? undefined : gensym___56045278.stateVar11)})) ?? (0.0))); - this.__backing_stateVar12 = new StateDecoratedVariable | Per>("stateVar12", ((({let gensym___164759887 = initializers; + this.__backing_stateVar12 = STATE_MGMT_FACTORY.makeState<(Set | Per)>(this, "stateVar12", ((({let gensym___164759887 = initializers; (((gensym___164759887) == (null)) ? undefined : gensym___164759887.stateVar12)})) ?? (new Per(6)))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_stateVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_stateVar1?: IStateDecoratedVariable; + public get stateVar1(): Per { return this.__backing_stateVar1!.get(); } + public set stateVar1(value: Per) { this.__backing_stateVar1!.set(value); } - private __backing_stateVar2?: StateDecoratedVariable>; + + private __backing_stateVar2?: IStateDecoratedVariable>; + public get stateVar2(): Array { return this.__backing_stateVar2!.get(); } + public set stateVar2(value: Array) { this.__backing_stateVar2!.set(value); } - private __backing_stateVar3?: StateDecoratedVariable; + + private __backing_stateVar3?: IStateDecoratedVariable; + public get stateVar3(): StateType { return this.__backing_stateVar3!.get(); } + public set stateVar3(value: StateType) { this.__backing_stateVar3!.set(value); } - private __backing_stateVar4?: StateDecoratedVariable>; + + private __backing_stateVar4?: IStateDecoratedVariable>; + public get stateVar4(): Set { return this.__backing_stateVar4!.get(); } + public set stateVar4(value: Set) { this.__backing_stateVar4!.set(value); } - private __backing_stateVar5?: StateDecoratedVariable>; + + private __backing_stateVar5?: IStateDecoratedVariable>; + public get stateVar5(): Array { return this.__backing_stateVar5!.get(); } + public set stateVar5(value: Array) { this.__backing_stateVar5!.set(value); } - private __backing_stateVar6?: StateDecoratedVariable>; + + private __backing_stateVar6?: IStateDecoratedVariable>; + public get stateVar6(): Array { return this.__backing_stateVar6!.get(); } + public set stateVar6(value: Array) { this.__backing_stateVar6!.set(value); } - private __backing_stateVar7?: StateDecoratedVariable>; + + private __backing_stateVar7?: IStateDecoratedVariable>; + public get stateVar7(): Array { return this.__backing_stateVar7!.get(); } + public set stateVar7(value: Array) { this.__backing_stateVar7!.set(value); } - private __backing_stateVar8?: StateDecoratedVariable<((sr: string)=> void)>; + + private __backing_stateVar8?: IStateDecoratedVariable<((sr: string)=> void)>; + public get stateVar8(): ((sr: string)=> void) { return this.__backing_stateVar8!.get(); } + public set stateVar8(value: ((sr: string)=> void)) { this.__backing_stateVar8!.set(value); } - private __backing_stateVar9?: StateDecoratedVariable; + + private __backing_stateVar9?: IStateDecoratedVariable; + public get stateVar9(): Date { return this.__backing_stateVar9!.get(); } + public set stateVar9(value: Date) { this.__backing_stateVar9!.set(value); } - private __backing_stateVar10?: StateDecoratedVariable>; + + private __backing_stateVar10?: IStateDecoratedVariable>; + public get stateVar10(): Map { return this.__backing_stateVar10!.get(); } + public set stateVar10(value: Map) { this.__backing_stateVar10!.set(value); } - private __backing_stateVar11?: StateDecoratedVariable; - public get stateVar11(): string | number { + + private __backing_stateVar11?: IStateDecoratedVariable<(string | number)>; + + public get stateVar11(): (string | number) { return this.__backing_stateVar11!.get(); } - public set stateVar11(value: string | number) { + + public set stateVar11(value: (string | number)) { this.__backing_stateVar11!.set(value); } - private __backing_stateVar12?: StateDecoratedVariable | Per>; - public get stateVar12(): Set | Per { + + private __backing_stateVar12?: IStateDecoratedVariable<(Set | Per)>; + + public get stateVar12(): (Set | Per) { return this.__backing_stateVar12!.get(); } - public set stateVar12(value: Set | Per) { + + public set stateVar12(value: (Set | Per)) { this.__backing_stateVar12!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void {} + + @memo() public build() {} + public constructor() {} + } -interface __Options_Parent { - set stateVar1(stateVar1: Per | undefined) - get stateVar1(): Per | undefined - set __backing_stateVar1(__backing_stateVar1: StateDecoratedVariable | undefined) - get __backing_stateVar1(): StateDecoratedVariable | undefined - set stateVar2(stateVar2: Array | undefined) - get stateVar2(): Array | undefined - set __backing_stateVar2(__backing_stateVar2: StateDecoratedVariable> | undefined) - get __backing_stateVar2(): StateDecoratedVariable> | undefined - set stateVar3(stateVar3: StateType | undefined) - get stateVar3(): StateType | undefined - set __backing_stateVar3(__backing_stateVar3: StateDecoratedVariable | undefined) - get __backing_stateVar3(): StateDecoratedVariable | undefined - set stateVar4(stateVar4: Set | undefined) - get stateVar4(): Set | undefined - set __backing_stateVar4(__backing_stateVar4: StateDecoratedVariable> | undefined) - get __backing_stateVar4(): StateDecoratedVariable> | undefined - set stateVar5(stateVar5: Array | undefined) - get stateVar5(): Array | undefined - set __backing_stateVar5(__backing_stateVar5: StateDecoratedVariable> | undefined) - get __backing_stateVar5(): StateDecoratedVariable> | undefined - set stateVar6(stateVar6: Array | undefined) - get stateVar6(): Array | undefined - set __backing_stateVar6(__backing_stateVar6: StateDecoratedVariable> | undefined) - get __backing_stateVar6(): StateDecoratedVariable> | undefined - set stateVar7(stateVar7: Array | undefined) - get stateVar7(): Array | undefined - set __backing_stateVar7(__backing_stateVar7: StateDecoratedVariable> | undefined) - get __backing_stateVar7(): StateDecoratedVariable> | undefined - set stateVar8(stateVar8: ((sr: string)=> void) | undefined) - get stateVar8(): ((sr: string)=> void) | undefined - set __backing_stateVar8(__backing_stateVar8: StateDecoratedVariable<((sr: string)=> void)> | undefined) - get __backing_stateVar8(): StateDecoratedVariable<((sr: string)=> void)> | undefined - set stateVar9(stateVar9: Date | undefined) - get stateVar9(): Date | undefined - set __backing_stateVar9(__backing_stateVar9: StateDecoratedVariable | undefined) - get __backing_stateVar9(): StateDecoratedVariable | undefined - set stateVar10(stateVar10: Map | undefined) - get stateVar10(): Map | undefined - set __backing_stateVar10(__backing_stateVar10: StateDecoratedVariable> | undefined) - get __backing_stateVar10(): StateDecoratedVariable> | undefined - set stateVar11(stateVar11: string | number | undefined) - get stateVar11(): string | number | undefined - set __backing_stateVar11(__backing_stateVar11: StateDecoratedVariable | undefined) - get __backing_stateVar11(): StateDecoratedVariable | undefined - set stateVar12(stateVar12: Set | Per | undefined) - get stateVar12(): Set | Per | undefined - set __backing_stateVar12(__backing_stateVar12: StateDecoratedVariable | Per> | undefined) - get __backing_stateVar12(): StateDecoratedVariable | Per> | undefined +@Component() export interface __Options_Parent { + set stateVar1(stateVar1: (Per | undefined)) + + get stateVar1(): (Per | undefined) + set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (Array | undefined)) + + get stateVar2(): (Array | undefined) + set __backing_stateVar2(__backing_stateVar2: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar2(): (IStateDecoratedVariable> | undefined) + set stateVar3(stateVar3: (StateType | undefined)) + + get stateVar3(): (StateType | undefined) + set __backing_stateVar3(__backing_stateVar3: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar3(): (IStateDecoratedVariable | undefined) + set stateVar4(stateVar4: (Set | undefined)) + + get stateVar4(): (Set | undefined) + set __backing_stateVar4(__backing_stateVar4: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar4(): (IStateDecoratedVariable> | undefined) + set stateVar5(stateVar5: (Array | undefined)) + + get stateVar5(): (Array | undefined) + set __backing_stateVar5(__backing_stateVar5: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar5(): (IStateDecoratedVariable> | undefined) + set stateVar6(stateVar6: (Array | undefined)) + + get stateVar6(): (Array | undefined) + set __backing_stateVar6(__backing_stateVar6: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar6(): (IStateDecoratedVariable> | undefined) + set stateVar7(stateVar7: (Array | undefined)) + + get stateVar7(): (Array | undefined) + set __backing_stateVar7(__backing_stateVar7: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar7(): (IStateDecoratedVariable> | undefined) + set stateVar8(stateVar8: (((sr: string)=> void) | undefined)) + + get stateVar8(): (((sr: string)=> void) | undefined) + set __backing_stateVar8(__backing_stateVar8: (IStateDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_stateVar8(): (IStateDecoratedVariable<((sr: string)=> void)> | undefined) + set stateVar9(stateVar9: (Date | undefined)) + + get stateVar9(): (Date | undefined) + set __backing_stateVar9(__backing_stateVar9: (IStateDecoratedVariable | undefined)) + + get __backing_stateVar9(): (IStateDecoratedVariable | undefined) + set stateVar10(stateVar10: (Map | undefined)) + + get stateVar10(): (Map | undefined) + set __backing_stateVar10(__backing_stateVar10: (IStateDecoratedVariable> | undefined)) + + get __backing_stateVar10(): (IStateDecoratedVariable> | undefined) + set stateVar11(stateVar11: ((string | number) | undefined)) + + get stateVar11(): ((string | number) | undefined) + set __backing_stateVar11(__backing_stateVar11: (IStateDecoratedVariable<(string | number)> | undefined)) + + get __backing_stateVar11(): (IStateDecoratedVariable<(string | number)> | undefined) + set stateVar12(stateVar12: ((Set | Per) | undefined)) + + get stateVar12(): ((Set | Per) | undefined) + set __backing_stateVar12(__backing_stateVar12: (IStateDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_stateVar12(): (IStateDecoratedVariable<(Set | Per)> | undefined) + } + `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -278,9 +370,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test complex type @State decorated variables transformation', - [parsedTransform, structNoRecheck], + [parsedTransform, structNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts index 52a438991ebe2faf37081d983e01c4e88afff32c..45f033642e618ebaa5f139698b9bc9b9f55473f5 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts @@ -14,19 +14,20 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; -const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/state'; +const STATE_DIR_PATH: string = 'decorators/state'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-state.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-to-state.ets'), ]; const pluginTester = new PluginTester('test @State decorated variables passing', buildConfig); @@ -37,83 +38,110 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + import { memo as memo } from "arkui.stateManagement.runtime"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; + + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, Column as Column, Text as Text } from "@ohos.arkui.component"; + import { State as State } from "@ohos.arkui.stateManagement"; function main() {} + + class Per { public str: string; + public constructor(str: string) { this.str = str; } + } -@Component({freezeWhenInactive:false}) final class Parent extends CustomComponent { - public __initializeStruct(initializers: __Options_Parent | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_parentVar1 = new StateDecoratedVariable("parentVar1", ((({let gensym___247315634 = initializers; +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_parentVar1 = STATE_MGMT_FACTORY.makeState(this, "parentVar1", ((({let gensym___247315634 = initializers; (((gensym___247315634) == (null)) ? undefined : gensym___247315634.parentVar1)})) ?? (new Per("hello")))); } - public __updateStruct(initializers: __Options_Parent | undefined): void {} - private __backing_parentVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_parentVar1?: IStateDecoratedVariable; + public get parentVar1(): Per { return this.__backing_parentVar1!.get(); } + public set parentVar1(value: Per) { this.__backing_parentVar1!.set(value); } - @memo() public _build(@memo() style: ((instance: Parent)=> Parent) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Parent | undefined): void { - Column(undefined, undefined, (() => { + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); - }), ({ + }), { childVar1: this.parentVar1, - } as __Options_Child), undefined, undefined); + }, undefined, undefined); })); } + public constructor() {} + } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_childVar1 = new StateDecoratedVariable("childVar1", ((({let gensym___218939886 = initializers; +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_childVar1 = STATE_MGMT_FACTORY.makeState(this, "childVar1", ((({let gensym___218939886 = initializers; (((gensym___218939886) == (null)) ? undefined : gensym___218939886.childVar1)})) ?? (new Per("ccc")))); } - public __updateStruct(initializers: __Options_Child | undefined): void {} - private __backing_childVar1?: StateDecoratedVariable; + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_childVar1?: IStateDecoratedVariable; + public get childVar1(): Per { return this.__backing_childVar1!.get(); } + public set childVar1(value: Per) { this.__backing_childVar1!.set(value); } - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void { + + @memo() public build() { Text(undefined, this.childVar1.str, undefined, undefined); } + public constructor() {} } -interface __Options_Parent { - set parentVar1(parentVar1: Per | undefined) - get parentVar1(): Per | undefined - set __backing_parentVar1(__backing_parentVar1: StateDecoratedVariable | undefined) - get __backing_parentVar1(): StateDecoratedVariable | undefined +@Component() export interface __Options_Parent { + set parentVar1(parentVar1: (Per | undefined)) + + get parentVar1(): (Per | undefined) + set __backing_parentVar1(__backing_parentVar1: (IStateDecoratedVariable | undefined)) + + get __backing_parentVar1(): (IStateDecoratedVariable | undefined) + } -interface __Options_Child { - set childVar1(childVar1: Per | undefined) - get childVar1(): Per | undefined - set __backing_childVar1(__backing_childVar1: StateDecoratedVariable | undefined) - get __backing_childVar1(): StateDecoratedVariable | undefined -} +@Component() export interface __Options_Child { + set childVar1(childVar1: (Per | undefined)) + + get childVar1(): (Per | undefined) + set __backing_childVar1(__backing_childVar1: (IStateDecoratedVariable | undefined)) + + get __backing_childVar1(): (IStateDecoratedVariable | undefined) + +} `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { @@ -122,9 +150,9 @@ function testParsedAndCheckedTransformer(this: PluginTestContext): void { pluginTester.run( 'test @State decorated variables passing', - [parsedTransform, uiNoRecheck], + [parsedTransform, uiNoRecheck, recheck], { - checked: [testParsedAndCheckedTransformer], + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts index 7f4460286083061c950d56618195e60c28aeac00..42f22ac6e680a5a0daa68af6cbddaae223bcf8fb 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,24 +38,15 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink with appstorage', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; - -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; - +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry, Column as Column, Text as Text, ClickEvent as ClickEvent } from "@ohos.arkui.component"; - import { StorageLink as StorageLink, AppStorage as AppStorage } from "@ohos.arkui.stateManagement"; function main() {} @@ -62,24 +54,30 @@ function main() {} AppStorage.setOrCreate("PropA", 47); AppStorage.setOrCreate("PropB", new Data(50)); +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-appstorage", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-appstorage", + integratedHsp: "false", + } as NavInterface)); + class Data { public code: number; - public constructor(code: number) { this.code = code; } - } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_storageLink = new StorageLinkDecoratedVariable("PropA", "storageLink", 1) - this.__backing_storageLinkObject = new StorageLinkDecoratedVariable("PropB", "storageLinkObject", new Data(1)) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_storageLink = STATE_MGMT_FACTORY.makeStorageLink(this, "PropA", "storageLink", 1) + this.__backing_storageLinkObject = STATE_MGMT_FACTORY.makeStorageLink(this, "PropB", "storageLinkObject", new Data(1)) } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_storageLink?: StorageLinkDecoratedVariable; + private __backing_storageLink?: IStorageLinkDecoratedVariable; public get storageLink(): number { return this.__backing_storageLink!.get(); @@ -89,7 +87,7 @@ class Data { this.__backing_storageLink!.set(value); } - private __backing_storageLinkObject?: StorageLinkDecoratedVariable; + private __backing_storageLinkObject?: IStorageLinkDecoratedVariable; public get storageLinkObject(): Data { return this.__backing_storageLinkObject!.get(); @@ -99,15 +97,15 @@ class Data { this.__backing_storageLinkObject!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLink += 1; })); return; }), \`From AppStorage \${this.storageLink}\`, undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLinkObject.code += 1; })); @@ -120,19 +118,19 @@ class Data { } -interface __Options_Index { - set storageLink(storageLink: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageLink(storageLink: (number | undefined)) - get storageLink(): number | undefined - set __backing_storageLink(__backing_storageLink: StorageLinkDecoratedVariable | undefined) + get storageLink(): (number | undefined) + set __backing_storageLink(__backing_storageLink: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storageLink(): StorageLinkDecoratedVariable | undefined - set storageLinkObject(storageLinkObject: Data | undefined) + get __backing_storageLink(): (IStorageLinkDecoratedVariable | undefined) + set storageLinkObject(storageLinkObject: (Data | undefined)) - get storageLinkObject(): Data | undefined - set __backing_storageLinkObject(__backing_storageLinkObject: StorageLinkDecoratedVariable | undefined) + get storageLinkObject(): (Data | undefined) + set __backing_storageLinkObject(__backing_storageLinkObject: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storageLinkObject(): StorageLinkDecoratedVariable | undefined + get __backing_storageLinkObject(): (IStorageLinkDecoratedVariable | undefined) } @@ -154,9 +152,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink with appstorage', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts index dba913594e47878ab3d517d21adb6da488833a00..f28433b8f357d47610992cb6d8b27c91f917febd 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink complex type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageLink as StorageLink } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-complex-type", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public name: string = ""; @@ -132,21 +133,20 @@ final class Status extends BaseEnum { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_arrayA = new StorageLinkDecoratedVariable>("Prop1", "arrayA", [1, 2, 3]) - this.__backing_objectA = new StorageLinkDecoratedVariable("Prop2", "objectA", {}) - this.__backing_dateA = new StorageLinkDecoratedVariable("Prop3", "dateA", new Date("2021-08-08")) - this.__backing_setA = new StorageLinkDecoratedVariable>("Prop4", "setA", new Set()) - this.__backing_mapA = new StorageLinkDecoratedVariable>("Prop5", "mapA", new Map()) - this.__backing_unionA = new StorageLinkDecoratedVariable("Prop6", "unionA", "") - this.__backing_classA = new StorageLinkDecoratedVariable("Prop7", "classA", new Person("John")) - this.__backing_enumA = new StorageLinkDecoratedVariable("Prop8", "enumA", Status.NotFound) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop1", "arrayA", [1, 2, 3]) + this.__backing_objectA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "objectA", {}) + this.__backing_dateA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "dateA", new Date("2021-08-08")) + this.__backing_setA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop4", "setA", new Set()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop5", "mapA", new Map()) + this.__backing_classA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop7", "classA", new Person("John")) + this.__backing_enumA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop8", "enumA", Status.NotFound) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_arrayA?: StorageLinkDecoratedVariable>; + private __backing_arrayA?: IStorageLinkDecoratedVariable>; public get arrayA(): Array { return this.__backing_arrayA!.get(); @@ -156,7 +156,7 @@ final class Status extends BaseEnum { this.__backing_arrayA!.set(value); } - private __backing_objectA?: StorageLinkDecoratedVariable; + private __backing_objectA?: IStorageLinkDecoratedVariable; public get objectA(): Object { return this.__backing_objectA!.get(); @@ -166,7 +166,7 @@ final class Status extends BaseEnum { this.__backing_objectA!.set(value); } - private __backing_dateA?: StorageLinkDecoratedVariable; + private __backing_dateA?: IStorageLinkDecoratedVariable; public get dateA(): Date { return this.__backing_dateA!.get(); @@ -176,7 +176,7 @@ final class Status extends BaseEnum { this.__backing_dateA!.set(value); } - private __backing_setA?: StorageLinkDecoratedVariable>; + private __backing_setA?: IStorageLinkDecoratedVariable>; public get setA(): Set { return this.__backing_setA!.get(); @@ -186,7 +186,7 @@ final class Status extends BaseEnum { this.__backing_setA!.set(value); } - private __backing_mapA?: StorageLinkDecoratedVariable>; + private __backing_mapA?: IStorageLinkDecoratedVariable>; public get mapA(): Map { return this.__backing_mapA!.get(); @@ -196,17 +196,7 @@ final class Status extends BaseEnum { this.__backing_mapA!.set(value); } - private __backing_unionA?: StorageLinkDecoratedVariable; - - public get unionA(): string | undefined { - return this.__backing_unionA!.get(); - } - - public set unionA(value: string | undefined) { - this.__backing_unionA!.set(value); - } - - private __backing_classA?: StorageLinkDecoratedVariable; + private __backing_classA?: IStorageLinkDecoratedVariable; public get classA(): Person { return this.__backing_classA!.get(); @@ -216,7 +206,7 @@ final class Status extends BaseEnum { this.__backing_classA!.set(value); } - private __backing_enumA?: StorageLinkDecoratedVariable; + private __backing_enumA?: IStorageLinkDecoratedVariable; public get enumA(): Status { return this.__backing_enumA!.get(); @@ -226,61 +216,55 @@ final class Status extends BaseEnum { this.__backing_enumA!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set arrayA(arrayA: Array | undefined) - - get arrayA(): Array | undefined - set __backing_arrayA(__backing_arrayA: StorageLinkDecoratedVariable> | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayA(arrayA: (Array | undefined)) - get __backing_arrayA(): StorageLinkDecoratedVariable> | undefined - set objectA(objectA: Object | undefined) + get arrayA(): (Array | undefined) + set __backing_arrayA(__backing_arrayA: (IStorageLinkDecoratedVariable> | undefined)) - get objectA(): Object | undefined - set __backing_objectA(__backing_objectA: StorageLinkDecoratedVariable | undefined) + get __backing_arrayA(): (IStorageLinkDecoratedVariable> | undefined) + set objectA(objectA: (Object | undefined)) - get __backing_objectA(): StorageLinkDecoratedVariable | undefined - set dateA(dateA: Date | undefined) + get objectA(): (Object | undefined) + set __backing_objectA(__backing_objectA: (IStorageLinkDecoratedVariable | undefined)) - get dateA(): Date | undefined - set __backing_dateA(__backing_dateA: StorageLinkDecoratedVariable | undefined) + get __backing_objectA(): (IStorageLinkDecoratedVariable | undefined) + set dateA(dateA: (Date | undefined)) - get __backing_dateA(): StorageLinkDecoratedVariable | undefined - set setA(setA: Set | undefined) + get dateA(): (Date | undefined) + set __backing_dateA(__backing_dateA: (IStorageLinkDecoratedVariable | undefined)) - get setA(): Set | undefined - set __backing_setA(__backing_setA: StorageLinkDecoratedVariable> | undefined) + get __backing_dateA(): (IStorageLinkDecoratedVariable | undefined) + set setA(setA: (Set | undefined)) - get __backing_setA(): StorageLinkDecoratedVariable> | undefined - set mapA(mapA: Map | undefined) + get setA(): (Set | undefined) + set __backing_setA(__backing_setA: (IStorageLinkDecoratedVariable> | undefined)) - get mapA(): Map | undefined - set __backing_mapA(__backing_mapA: StorageLinkDecoratedVariable> | undefined) + get __backing_setA(): (IStorageLinkDecoratedVariable> | undefined) + set mapA(mapA: (Map | undefined)) - get __backing_mapA(): StorageLinkDecoratedVariable> | undefined - set unionA(unionA: string | undefined | undefined) + get mapA(): (Map | undefined) + set __backing_mapA(__backing_mapA: (IStorageLinkDecoratedVariable> | undefined)) - get unionA(): string | undefined | undefined - set __backing_unionA(__backing_unionA: StorageLinkDecoratedVariable | undefined) + get __backing_mapA(): (IStorageLinkDecoratedVariable> | undefined) + set classA(classA: (Person | undefined)) - get __backing_unionA(): StorageLinkDecoratedVariable | undefined - set classA(classA: Person | undefined) + get classA(): (Person | undefined) + set __backing_classA(__backing_classA: (IStorageLinkDecoratedVariable | undefined)) - get classA(): Person | undefined - set __backing_classA(__backing_classA: StorageLinkDecoratedVariable | undefined) + get __backing_classA(): (IStorageLinkDecoratedVariable | undefined) + set enumA(enumA: (Status | undefined)) - get __backing_classA(): StorageLinkDecoratedVariable | undefined - set enumA(enumA: Status | undefined) + get enumA(): (Status | undefined) + set __backing_enumA(__backing_enumA: (IStorageLinkDecoratedVariable | undefined)) - get enumA(): Status | undefined - set __backing_enumA(__backing_enumA: StorageLinkDecoratedVariable | undefined) - - get __backing_enumA(): StorageLinkDecoratedVariable | undefined + get __backing_enumA(): (IStorageLinkDecoratedVariable | undefined) } @@ -294,7 +278,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStorageLinkTransformer(this: PluginTestContext): void { @@ -303,9 +286,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink complex type transform', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts index 27a78307e6794b6a40d9087fb97ee33800cee8c1..923664b6c5c56e850494f828a1d110d0a2b3b28d 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,36 +38,36 @@ const storageLinkTransform: Plugins = { const pluginTester = new PluginTester('test storagelink primitive type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageLink as StorageLink } from "@ohos.arkui.stateManagement"; function main() {} - - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_numA = new StorageLinkDecoratedVariable("Prop1", "numA", 33) - this.__backing_stringA = new StorageLinkDecoratedVariable("Prop2", "stringA", "AA") - this.__backing_booleanA = new StorageLinkDecoratedVariable("Prop3", "booleanA", true) +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storagelink/storagelink-primitive-type", + pageFullPath: "test/demo/mock/decorators/storagelink/storagelink-primitive-type", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop1", "numA", 33) + this.__backing_stringA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "stringA", "AA") + this.__backing_booleanA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "booleanA", true) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_numA?: StorageLinkDecoratedVariable; + private __backing_numA?: IStorageLinkDecoratedVariable; public get numA(): number { return this.__backing_numA!.get(); @@ -76,7 +77,7 @@ function main() {} this.__backing_numA!.set(value); } - private __backing_stringA?: StorageLinkDecoratedVariable; + private __backing_stringA?: IStorageLinkDecoratedVariable; public get stringA(): string { return this.__backing_stringA!.get(); @@ -86,7 +87,7 @@ function main() {} this.__backing_stringA!.set(value); } - private __backing_booleanA?: StorageLinkDecoratedVariable; + private __backing_booleanA?: IStorageLinkDecoratedVariable; public get booleanA(): boolean { return this.__backing_booleanA!.get(); @@ -96,31 +97,31 @@ function main() {} this.__backing_booleanA!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set numA(numA: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numA(numA: (number | undefined)) - get numA(): number | undefined - set __backing_numA(__backing_numA: StorageLinkDecoratedVariable | undefined) + get numA(): (number | undefined) + set __backing_numA(__backing_numA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_numA(): StorageLinkDecoratedVariable | undefined - set stringA(stringA: string | undefined) + get __backing_numA(): (IStorageLinkDecoratedVariable | undefined) + set stringA(stringA: (string | undefined)) - get stringA(): string | undefined - set __backing_stringA(__backing_stringA: StorageLinkDecoratedVariable | undefined) + get stringA(): (string | undefined) + set __backing_stringA(__backing_stringA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_stringA(): StorageLinkDecoratedVariable | undefined - set booleanA(booleanA: boolean | undefined) + get __backing_stringA(): (IStorageLinkDecoratedVariable | undefined) + set booleanA(booleanA: (boolean | undefined)) - get booleanA(): boolean | undefined - set __backing_booleanA(__backing_booleanA: StorageLinkDecoratedVariable | undefined) + get booleanA(): (boolean | undefined) + set __backing_booleanA(__backing_booleanA: (IStorageLinkDecoratedVariable | undefined)) - get __backing_booleanA(): StorageLinkDecoratedVariable | undefined + get __backing_booleanA(): (IStorageLinkDecoratedVariable | undefined) } @@ -134,7 +135,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStorageLinkTransformer(this: PluginTestContext): void { @@ -143,9 +143,9 @@ function testStorageLinkTransformer(this: PluginTestContext): void { pluginTester.run( 'test storagelink primitive type transform', - [storageLinkTransform, uiNoRecheck], + [storageLinkTransform, uiNoRecheck, recheck], { - checked: [testStorageLinkTransformer], + 'checked:ui-no-recheck': [testStorageLinkTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c38a8280e284b45c1a66b934c21dab16ce6b7f82 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-complex-type.test.ts @@ -0,0 +1,281 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-ref-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @StoragePropRef complex type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { StoragePropRef as StoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayB?: IStoragePropRefDecoratedVariable>; + + public get arrayB(): Array { + return this.__backing_arrayB!.get(); + } + + public set arrayB(value: Array) { + this.__backing_arrayB!.set(value); + } + + private __backing_objectB?: IStoragePropRefDecoratedVariable; + + public get objectB(): Object { + return this.__backing_objectB!.get(); + } + + public set objectB(value: Object) { + this.__backing_objectB!.set(value); + } + + private __backing_dateB?: IStoragePropRefDecoratedVariable; + + public get dateB(): Date { + return this.__backing_dateB!.get(); + } + + public set dateB(value: Date) { + this.__backing_dateB!.set(value); + } + + private __backing_setB?: IStoragePropRefDecoratedVariable>; + + public get setB(): Set { + return this.__backing_setB!.get(); + } + + public set setB(value: Set) { + this.__backing_setB!.set(value); + } + + private __backing_mapB?: IStoragePropRefDecoratedVariable>; + + public get mapB(): Map { + return this.__backing_mapB!.get(); + } + + public set mapB(value: Map) { + this.__backing_mapB!.set(value); + } + + private __backing_classB?: IStoragePropRefDecoratedVariable; + + public get classB(): Person { + return this.__backing_classB!.get(); + } + + public set classB(value: Person) { + this.__backing_classB!.set(value); + } + + private __backing_enumB?: IStoragePropRefDecoratedVariable; + + public get enumB(): Status { + return this.__backing_enumB!.get(); + } + + public set enumB(value: Status) { + this.__backing_enumB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) + + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_arrayB(): (IStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) + + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_objectB(): (IStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) + + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_dateB(): (IStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) + + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_setB(): (IStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) + + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_mapB(): (IStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) + + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_classB(): (IStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) + + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_enumB(): (IStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @StoragePropRef complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..31d27dd460e696ad5a4a97493c5ff99676f0280a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop-ref/storageprop-ref-primitive-type.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop-ref'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-ref-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test @StoragePropRef primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { StoragePropRef as StoragePropRef } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false) + this.__backing_undefinedB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop4", "undefinedB", undefined) + this.__backing_nullB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop5", "nullB", null) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numB?: IStoragePropRefDecoratedVariable; + + public get numB(): number { + return this.__backing_numB!.get(); + } + + public set numB(value: number) { + this.__backing_numB!.set(value); + } + + private __backing_stringB?: IStoragePropRefDecoratedVariable; + + public get stringB(): string { + return this.__backing_stringB!.get(); + } + + public set stringB(value: string) { + this.__backing_stringB!.set(value); + } + + private __backing_booleanB?: IStoragePropRefDecoratedVariable; + + public get booleanB(): boolean { + return this.__backing_booleanB!.get(); + } + + public set booleanB(value: boolean) { + this.__backing_booleanB!.set(value); + } + + private __backing_undefinedB?: IStoragePropRefDecoratedVariable; + + public get undefinedB(): undefined { + return this.__backing_undefinedB!.get(); + } + + public set undefinedB(value: undefined) { + this.__backing_undefinedB!.set(value); + } + + private __backing_nullB?: IStoragePropRefDecoratedVariable; + + public get nullB(): null { + return this.__backing_nullB!.get(); + } + + public set nullB(value: null) { + this.__backing_nullB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) + + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_numB(): (IStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) + + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_stringB(): (IStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) + + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_booleanB(): (IStoragePropRefDecoratedVariable | undefined) + set undefinedB(undefinedB: (undefined | undefined)) + + get undefinedB(): (undefined | undefined) + set __backing_undefinedB(__backing_undefinedB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_undefinedB(): (IStoragePropRefDecoratedVariable | undefined) + set nullB(nullB: (null | undefined)) + + get nullB(): (null | undefined) + set __backing_nullB(__backing_nullB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_nullB(): (IStoragePropRefDecoratedVariable | undefined) + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @StoragePropRef primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts index e00d66d86c692d3cf2f4eee7eb9c4c44afc9ca5d..374e2187be4c66fdb21e1694899248389bafd1a1 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,20 +38,21 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop with appstorage', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; -import { UITextAttribute as UITextAttribute } from "@ohos.arkui.component"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { UIColumnAttribute as UIColumnAttribute } from "@ohos.arkui.component"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry, Column as Column, Text as Text, ClickEvent as ClickEvent } from "@ohos.arkui.component"; @@ -62,6 +64,14 @@ function main() {} AppStorage.setOrCreate("PropA", 47); AppStorage.setOrCreate("PropB", new Data(50)); +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-appstorage", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-appstorage", + integratedHsp: "false", + } as NavInterface)); + class Data { public code: number; @@ -71,15 +81,15 @@ class Data { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class Index extends CustomComponent { - public __initializeStruct(initializers: __Options_Index | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_storageProp = new StoragePropDecoratedVariable("PropA", "storageProp", 1) - this.__backing_storagePropObject = new StoragePropDecoratedVariable("PropB", "storagePropObject", new Data(1)) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_storageProp = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropA", "storageProp", 1) + this.__backing_storagePropObject = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropB", "storagePropObject", new Data(1)) } - public __updateStruct(initializers: __Options_Index | undefined): void {} + public __updateStruct(initializers: (__Options_Index | undefined)): void {} - private __backing_storageProp?: StoragePropDecoratedVariable; + private __backing_storageProp?: IStoragePropRefDecoratedVariable; public get storageProp(): number { return this.__backing_storageProp!.get(); @@ -89,7 +99,7 @@ class Data { this.__backing_storageProp!.set(value); } - private __backing_storagePropObject?: StoragePropDecoratedVariable; + private __backing_storagePropObject?: IStoragePropRefDecoratedVariable; public get storagePropObject(): Data { return this.__backing_storagePropObject!.get(); @@ -99,15 +109,15 @@ class Data { this.__backing_storagePropObject!.set(value); } - @memo() public _build(@memo() style: ((instance: Index)=> Index) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Index | undefined): void { - Column(undefined, undefined, (() => { - Text(@memo() ((instance: UITextAttribute): void => { + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageProp += 1; })); return; }), \`From AppStorage \${this.storageProp}\`, undefined, undefined); - Text(@memo() ((instance: UITextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storagePropObject.code += 1; })); @@ -120,19 +130,19 @@ class Data { } -interface __Options_Index { - set storageProp(storageProp: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageProp(storageProp: (number | undefined)) - get storageProp(): number | undefined - set __backing_storageProp(__backing_storageProp: StoragePropDecoratedVariable | undefined) + get storageProp(): (number | undefined) + set __backing_storageProp(__backing_storageProp: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storageProp(): StoragePropDecoratedVariable | undefined - set storagePropObject(storagePropObject: Data | undefined) + get __backing_storageProp(): (IStoragePropRefDecoratedVariable | undefined) + set storagePropObject(storagePropObject: (Data | undefined)) - get storagePropObject(): Data | undefined - set __backing_storagePropObject(__backing_storagePropObject: StoragePropDecoratedVariable | undefined) + get storagePropObject(): (Data | undefined) + set __backing_storagePropObject(__backing_storagePropObject: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storagePropObject(): StoragePropDecoratedVariable | undefined + get __backing_storagePropObject(): (IStoragePropRefDecoratedVariable | undefined) } @@ -146,7 +156,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testStoragePropTransformer(this: PluginTestContext): void { @@ -155,9 +164,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop with appstorage', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts index ca212c7caad25e27955d4136240a47414626b8f9..65aea99c9f412cc1e5eaf02e11704cacf08b8ae6 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,25 +38,25 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop complex type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; - -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; - import { memo as memo } from "arkui.stateManagement.runtime"; - -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; - +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; - import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; - import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; - import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; function main() {} - +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-complex-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-complex-type", + integratedHsp: "false", + } as NavInterface)); class Person { public name: string = ""; @@ -132,21 +133,20 @@ final class Status extends BaseEnum { } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_arrayB = new StoragePropDecoratedVariable>("Prop1", "arrayB", [1, 2, 3]) - this.__backing_objectB = new StoragePropDecoratedVariable("Prop2", "objectB", {}) - this.__backing_dateB = new StoragePropDecoratedVariable("Prop3", "dateB", new Date("2021-09-09")) - this.__backing_setB = new StoragePropDecoratedVariable>("Prop4", "setB", new Set()) - this.__backing_mapB = new StoragePropDecoratedVariable>("Prop5", "mapB", new Map()) - this.__backing_unionB = new StoragePropDecoratedVariable("Prop6", "unionB", "") - this.__backing_classB = new StoragePropDecoratedVariable("Prop7", "classB", new Person("Kevin")) - this.__backing_enumB = new StoragePropDecoratedVariable("Prop8", "enumB", Status.NotFound) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_arrayB?: StoragePropDecoratedVariable>; + private __backing_arrayB?: IStoragePropRefDecoratedVariable>; public get arrayB(): Array { return this.__backing_arrayB!.get(); @@ -156,7 +156,7 @@ final class Status extends BaseEnum { this.__backing_arrayB!.set(value); } - private __backing_objectB?: StoragePropDecoratedVariable; + private __backing_objectB?: IStoragePropRefDecoratedVariable; public get objectB(): Object { return this.__backing_objectB!.get(); @@ -166,7 +166,7 @@ final class Status extends BaseEnum { this.__backing_objectB!.set(value); } - private __backing_dateB?: StoragePropDecoratedVariable; + private __backing_dateB?: IStoragePropRefDecoratedVariable; public get dateB(): Date { return this.__backing_dateB!.get(); @@ -176,7 +176,7 @@ final class Status extends BaseEnum { this.__backing_dateB!.set(value); } - private __backing_setB?: StoragePropDecoratedVariable>; + private __backing_setB?: IStoragePropRefDecoratedVariable>; public get setB(): Set { return this.__backing_setB!.get(); @@ -186,7 +186,7 @@ final class Status extends BaseEnum { this.__backing_setB!.set(value); } - private __backing_mapB?: StoragePropDecoratedVariable>; + private __backing_mapB?: IStoragePropRefDecoratedVariable>; public get mapB(): Map { return this.__backing_mapB!.get(); @@ -196,17 +196,7 @@ final class Status extends BaseEnum { this.__backing_mapB!.set(value); } - private __backing_unionB?: StoragePropDecoratedVariable; - - public get unionB(): string | undefined { - return this.__backing_unionB!.get(); - } - - public set unionB(value: string | undefined) { - this.__backing_unionB!.set(value); - } - - private __backing_classB?: StoragePropDecoratedVariable; + private __backing_classB?: IStoragePropRefDecoratedVariable; public get classB(): Person { return this.__backing_classB!.get(); @@ -216,7 +206,7 @@ final class Status extends BaseEnum { this.__backing_classB!.set(value); } - private __backing_enumB?: StoragePropDecoratedVariable; + private __backing_enumB?: IStoragePropRefDecoratedVariable; public get enumB(): Status { return this.__backing_enumB!.get(); @@ -226,61 +216,55 @@ final class Status extends BaseEnum { this.__backing_enumB!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set arrayB(arrayB: Array | undefined) - - get arrayB(): Array | undefined - set __backing_arrayB(__backing_arrayB: StoragePropDecoratedVariable> | undefined) - - get __backing_arrayB(): StoragePropDecoratedVariable> | undefined - set objectB(objectB: Object | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) - get objectB(): Object | undefined - set __backing_objectB(__backing_objectB: StoragePropDecoratedVariable | undefined) + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_objectB(): StoragePropDecoratedVariable | undefined - set dateB(dateB: Date | undefined) + get __backing_arrayB(): (IStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) - get dateB(): Date | undefined - set __backing_dateB(__backing_dateB: StoragePropDecoratedVariable | undefined) + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_dateB(): StoragePropDecoratedVariable | undefined - set setB(setB: Set | undefined) + get __backing_objectB(): (IStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) - get setB(): Set | undefined - set __backing_setB(__backing_setB: StoragePropDecoratedVariable> | undefined) + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_setB(): StoragePropDecoratedVariable> | undefined - set mapB(mapB: Map | undefined) + get __backing_dateB(): (IStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) - get mapB(): Map | undefined - set __backing_mapB(__backing_mapB: StoragePropDecoratedVariable> | undefined) + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_mapB(): StoragePropDecoratedVariable> | undefined - set unionB(unionB: string | undefined | undefined) + get __backing_setB(): (IStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) - get unionB(): string | undefined | undefined - set __backing_unionB(__backing_unionB: StoragePropDecoratedVariable | undefined) + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (IStoragePropRefDecoratedVariable> | undefined)) - get __backing_unionB(): StoragePropDecoratedVariable | undefined - set classB(classB: Person | undefined) + get __backing_mapB(): (IStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) - get classB(): Person | undefined - set __backing_classB(__backing_classB: StoragePropDecoratedVariable | undefined) + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_classB(): StoragePropDecoratedVariable | undefined - set enumB(enumB: Status | undefined) + get __backing_classB(): (IStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) - get enumB(): Status | undefined - set __backing_enumB(__backing_enumB: StoragePropDecoratedVariable | undefined) + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_enumB(): StoragePropDecoratedVariable | undefined + get __backing_enumB(): (IStoragePropRefDecoratedVariable | undefined) } @@ -302,9 +286,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop complex type transform', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts index d6247f205625a78926fd1b6e745ddbaca0b1fd2a..660a51361e77b1c811511ebda2b255bc1a66e897 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,16 +38,19 @@ const storagePropTransform: Plugins = { const pluginTester = new PluginTester('test storageprop primitive type transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; @@ -55,18 +59,24 @@ import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; function main() {} - - -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_numB = new StoragePropDecoratedVariable("Prop1", "numB", 43) - this.__backing_stringB = new StoragePropDecoratedVariable("Prop2", "stringB", "BB") - this.__backing_booleanB = new StoragePropDecoratedVariable("Prop3", "booleanB", false) +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-primitive-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-primitive-type", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false) } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} - private __backing_numB?: StoragePropDecoratedVariable; + private __backing_numB?: IStoragePropRefDecoratedVariable; public get numB(): number { return this.__backing_numB!.get(); @@ -76,7 +86,7 @@ function main() {} this.__backing_numB!.set(value); } - private __backing_stringB?: StoragePropDecoratedVariable; + private __backing_stringB?: IStoragePropRefDecoratedVariable; public get stringB(): string { return this.__backing_stringB!.get(); @@ -86,7 +96,7 @@ function main() {} this.__backing_stringB!.set(value); } - private __backing_booleanB?: StoragePropDecoratedVariable; + private __backing_booleanB?: IStoragePropRefDecoratedVariable; public get booleanB(): boolean { return this.__backing_booleanB!.get(); @@ -96,31 +106,31 @@ function main() {} this.__backing_booleanB!.set(value); } - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set numB(numB: number | undefined) +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) - get numB(): number | undefined - set __backing_numB(__backing_numB: StoragePropDecoratedVariable | undefined) + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_numB(): StoragePropDecoratedVariable | undefined - set stringB(stringB: string | undefined) + get __backing_numB(): (IStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) - get stringB(): string | undefined - set __backing_stringB(__backing_stringB: StoragePropDecoratedVariable | undefined) + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_stringB(): StoragePropDecoratedVariable | undefined - set booleanB(booleanB: boolean | undefined) + get __backing_stringB(): (IStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) - get booleanB(): boolean | undefined - set __backing_booleanB(__backing_booleanB: StoragePropDecoratedVariable | undefined) + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_booleanB(): StoragePropDecoratedVariable | undefined + get __backing_booleanB(): (IStoragePropRefDecoratedVariable | undefined) } @@ -142,9 +152,9 @@ function testStoragePropTransformer(this: PluginTestContext): void { pluginTester.run( 'test storageprop primitive type transform', - [storagePropTransform, uiNoRecheck], + [storagePropTransform, uiNoRecheck, recheck], { - checked: [testStoragePropTransformer], + 'checked:ui-no-recheck': [testStoragePropTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts index fbb612c70fa628e4852f9fdc505881799e5d71aa..f38b5f6d65e8686a9fb15aac64cde388132b597d 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts @@ -14,11 +14,12 @@ */ import * as path from 'path'; -import { PluginTestContext, PluginTester } from '../../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../../utils/artkts-config'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; import { parseDumpSrc } from '../../../../utils/parse-string'; -import { uiNoRecheck } from '../../../../utils/plugins'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; import { uiTransform } from '../../../../../ui-plugins'; import { Plugins } from '../../../../../common/plugin-context'; @@ -37,58 +38,65 @@ const watchTransform: Plugins = { const pluginTester = new PluginTester('test basic watch transform', buildConfig); const expectedScript: string = ` -import { __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; -import { __memo_context_type as __memo_context_type } from "arkui.stateManagement.runtime"; +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { ConsumeDecoratedVariable as ConsumeDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; -import { ProvideDecoratedVariable as ProvideDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { SubscribedWatches as SubscribedWatches } from "@ohos.arkui.stateManagement"; +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; -import { WatchIdType as WatchIdType } from "@ohos.arkui.stateManagement"; +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { int32 as int32 } from "@ohos.arkui.stateManagement"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; -import { IObservedObject as IObservedObject } from "@ohos.arkui.stateManagement"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { setObservationDepth as setObservationDepth } from "@ohos.arkui.stateManagement"; +import { memo as memo } from "arkui.stateManagement.runtime"; -import { BackingValue as BackingValue } from "@ohos.arkui.stateManagement"; +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; -import { MutableStateMeta as MutableStateMeta } from "@ohos.arkui.stateManagement"; +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; -import { ObjectLinkDecoratedVariable as ObjectLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; -import { DecoratedV1VariableBase as DecoratedV1VariableBase } from "@ohos.arkui.stateManagement"; +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; -import { LinkDecoratedVariable as LinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; -import { StoragePropDecoratedVariable as StoragePropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; -import { StorageLinkDecoratedVariable as StorageLinkDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { PropDecoratedVariable as PropDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { NavInterface as NavInterface } from "arkui.UserView"; -import { StateDecoratedVariable as StateDecoratedVariable } from "@ohos.arkui.stateManagement"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; import { EntryPoint as EntryPoint } from "arkui.UserView"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; +import { Component as Component, Entry as Entry, Column as Column } from "@ohos.arkui.component"; import { State as State, Prop as Prop, StorageLink as StorageLink, StorageProp as StorageProp, Link as Link, Watch as Watch, ObjectLink as ObjectLink, Observed as Observed, Track as Track, Provide as Provide, Consume as Consume } from "@ohos.arkui.stateManagement"; function main() {} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/watch/watch-basic", + pageFullPath: "test/demo/mock/decorators/watch/watch-basic", + integratedHsp: "false", + } as NavInterface)); - -@Observed() class A implements IObservedObject { - private subscribedWatches: SubscribedWatches = new SubscribedWatches(); +@Observed() class A implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); public addWatchSubscriber(watchId: WatchIdType): void { this.subscribedWatches.addWatchSubscriber(watchId); @@ -102,63 +110,85 @@ function main() {} this.subscribedWatches.executeOnSubscribingWatches(propertyName); } - public _permissibleAddRefDepth: int32 = 0; + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } public propA: string = "hello"; - private __backing_trackA: string = "world"; + @JSONRename({newName:"trackA"}) private __backing_trackA: string = "world"; - private __meta_trackA: MutableStateMeta = new MutableStateMeta("@Track"); + @JSONStringifyIgnore() private __meta_trackA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); public constructor() {} public get trackA(): string { - if (((this._permissibleAddRefDepth) > (0))) { - this.__meta_trackA.addRef(); - } + this.conditionalAddRef(this.__meta_trackA); return this.__backing_trackA; } public set trackA(newValue: string) { if (((this.__backing_trackA) !== (newValue))) { this.__backing_trackA = newValue; - this.__meta_trackA.fireChange(); - this.executeOnSubscribingWatches("trackA"); + this.__meta_trackA.fireChange(); + this.executeOnSubscribingWatches("trackA"); } } } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component({freezeWhenInactive:false}) final class MyStateSample extends CustomComponent { - public __initializeStruct(initializers: __Options_MyStateSample | undefined, @memo() content: (()=> void) | undefined): void { - this.__backing_statevar = new StateDecoratedVariable("statevar", ((({let gensym___76198660 = initializers; +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_statevar = STATE_MGMT_FACTORY.makeState(this, "statevar", ((({let gensym___76198660 = initializers; (((gensym___76198660) == (null)) ? undefined : gensym___76198660.statevar)})) ?? ("Hello World")), ((_: string): void => { this.stateOnChange(_); })); - this.__backing_propvar = new PropDecoratedVariable("propvar", ((({let gensym___241486692 = initializers; + this.__backing_propvar = STATE_MGMT_FACTORY.makeProp(this, "propvar", ((({let gensym___241486692 = initializers; (((gensym___241486692) == (null)) ? undefined : gensym___241486692.propvar)})) ?? ("Hello World")), ((_: string): void => { this.propOnChange(_); })); - this.__backing_storagelinkvar = new StorageLinkDecoratedVariable("prop1", "storagelinkvar", "Hello World", ((_: string): void => { + if (({let gensym___165820150 = initializers; + (((gensym___165820150) == (null)) ? undefined : gensym___165820150.__backing_linkvar)})) { + this.__backing_linkvar = STATE_MGMT_FACTORY.makeLink(this, "linkvar", initializers!.__backing_linkvar!, ((_: string): void => { + this.linkOnChange(_); + })); + }; + this.__backing_storagelinkvar = STATE_MGMT_FACTORY.makeStorageLink(this, "prop1", "storagelinkvar", "Hello World", ((_: string): void => { this.storageLinkOnChange(_); })) - this.__backing_storagepropvar = new StoragePropDecoratedVariable("prop2", "storagepropvar", "Hello World", ((_: string): void => { + this.__backing_storagepropvar = STATE_MGMT_FACTORY.makeStoragePropRef(this, "prop2", "storagepropvar", "Hello World", ((_: string): void => { this.storagePropOnChange(_); })) - this.__backing_providevar = this.addProvidedVar("providevar", "providevar", ((({let gensym___194235814 = initializers; - (((gensym___194235814) == (null)) ? undefined : gensym___194235814.providevar)})) ?? ("Hello World")), false, ((_: string): void => { + this.__backing_objectlinkvar = STATE_MGMT_FACTORY.makeObjectLink(this, "objectlinkvar", (({let gensym___172556967 = initializers; + (((gensym___172556967) == (null)) ? undefined : gensym___172556967.objectlinkvar)}) as A), ((_: string): void => { + this.objectLinkOnChange(_); + })) + this.__backing_providevar = STATE_MGMT_FACTORY.makeProvide(this, "providevar", "providevar", ((({let gensym___244584558 = initializers; + (((gensym___244584558) == (null)) ? undefined : gensym___244584558.providevar)})) ?? ("Hello World")), false, ((_: string): void => { this.ProvideOnChange(_); })); } - public __updateStruct(initializers: __Options_MyStateSample | undefined): void { + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { if (((({let gensym___220608839 = initializers; (((gensym___220608839) == (null)) ? undefined : gensym___220608839.propvar)})) !== (undefined))) { this.__backing_propvar!.update((initializers!.propvar as string)); } + if (((({let gensym___164966179 = initializers; + (((gensym___164966179) == (null)) ? undefined : gensym___164966179.objectlinkvar)})) !== (undefined))) { + this.__backing_objectlinkvar!.update(initializers!.objectlinkvar!); + } } - private __backing_statevar?: StateDecoratedVariable; + private __backing_statevar?: IStateDecoratedVariable; public get statevar(): string { return this.__backing_statevar!.get(); @@ -168,7 +198,7 @@ function main() {} this.__backing_statevar!.set(value); } - private __backing_propvar?: PropDecoratedVariable; + private __backing_propvar?: IPropDecoratedVariable; public get propvar(): string { return this.__backing_propvar!.get(); @@ -178,7 +208,17 @@ function main() {} this.__backing_propvar!.set(value); } - private __backing_storagelinkvar?: StorageLinkDecoratedVariable; + private __backing_linkvar?: ILinkDecoratedVariable; + + public get linkvar(): string { + return this.__backing_linkvar!.get(); + } + + public set linkvar(value: string) { + this.__backing_linkvar!.set(value); + } + + private __backing_storagelinkvar?: IStorageLinkDecoratedVariable; public get storagelinkvar(): string { return this.__backing_storagelinkvar!.get(); @@ -188,7 +228,7 @@ function main() {} this.__backing_storagelinkvar!.set(value); } - private __backing_storagepropvar?: StoragePropDecoratedVariable; + private __backing_storagepropvar?: IStoragePropRefDecoratedVariable; public get storagepropvar(): string { return this.__backing_storagepropvar!.get(); @@ -198,7 +238,13 @@ function main() {} this.__backing_storagepropvar!.set(value); } - private __backing_providevar?: ProvideDecoratedVariable; + private __backing_objectlinkvar?: IObjectLinkDecoratedVariable; + + public get objectlinkvar(): A { + return this.__backing_objectlinkvar!.get(); + } + + private __backing_providevar?: IProvideDecoratedVariable; public get providevar(): string { return this.__backing_providevar!.get(); @@ -212,68 +258,110 @@ function main() {} public propOnChange(propName: string) {} + public linkOnChange(propName: string) {} + public storageLinkOnChange(propName: string) {} public storagePropOnChange(propName: string) {} + public objectLinkOnChange(propName: string) {} + public ProvideOnChange(propName: string) {} - @memo() public _build(@memo() style: ((instance: MyStateSample)=> MyStateSample) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_MyStateSample | undefined): void { - Child._instantiateImpl(undefined, (() => { - return new Child(); - }), undefined, undefined, undefined); + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + })); } public constructor() {} } -@Component({freezeWhenInactive:false}) final class Child extends CustomComponent { - public __initializeStruct(initializers: __Options_Child | undefined, @memo() content: (()=> void) | undefined): void {} +@Component() final struct Child extends CustomComponent { + public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_providevar = STATE_MGMT_FACTORY.makeConsume(this, "providevar", "providevar", ((_: string): void => { + this.ConsumeOnChange(_); + })); + } + + public __updateStruct(initializers: (__Options_Child | undefined)): void {} + + private __backing_providevar?: IConsumeDecoratedVariable; + + public get providevar(): string { + return this.__backing_providevar!.get(); + } + + public set providevar(value: string) { + this.__backing_providevar!.set(value); + } - public __updateStruct(initializers: __Options_Child | undefined): void {} + public ConsumeOnChange(propName: string) {} - @memo() public _build(@memo() style: ((instance: Child)=> Child) | undefined, @memo() content: (()=> void) | undefined, initializers: __Options_Child | undefined): void {} + @memo() public build() {} public constructor() {} } -interface __Options_MyStateSample { - set statevar(statevar: string | undefined) +@Retention({policy:"SOURCE"}) @interface __Link_intrinsic {} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set statevar(statevar: (string | undefined)) + + get statevar(): (string | undefined) + @Watch({value:"stateOnChange"}) set __backing_statevar(__backing_statevar: (IStateDecoratedVariable | undefined)) + + @Watch({value:"stateOnChange"}) get __backing_statevar(): (IStateDecoratedVariable | undefined) + set propvar(propvar: (string | undefined)) - get statevar(): string | undefined - set __backing_statevar(__backing_statevar: StateDecoratedVariable | undefined) + get propvar(): (string | undefined) + @Watch({value:"propOnChange"}) set __backing_propvar(__backing_propvar: (IPropDecoratedVariable | undefined)) - get __backing_statevar(): StateDecoratedVariable | undefined - set propvar(propvar: string | undefined) + @Watch({value:"propOnChange"}) get __backing_propvar(): (IPropDecoratedVariable | undefined) + @__Link_intrinsic() set linkvar(linkvar: (string | undefined)) - get propvar(): string | undefined - set __backing_propvar(__backing_propvar: PropDecoratedVariable | undefined) + @__Link_intrinsic() get linkvar(): (string | undefined) + @Watch({value:"linkOnChange"}) set __backing_linkvar(__backing_linkvar: (LinkSourceType | undefined)) - get __backing_propvar(): PropDecoratedVariable | undefined - set storagelinkvar(storagelinkvar: string | undefined) + @Watch({value:"linkOnChange"}) get __backing_linkvar(): (LinkSourceType | undefined) + set storagelinkvar(storagelinkvar: (string | undefined)) - get storagelinkvar(): string | undefined - set __backing_storagelinkvar(__backing_storagelinkvar: StorageLinkDecoratedVariable | undefined) + get storagelinkvar(): (string | undefined) + @Watch({value:"storageLinkOnChange"}) set __backing_storagelinkvar(__backing_storagelinkvar: (IStorageLinkDecoratedVariable | undefined)) - get __backing_storagelinkvar(): StorageLinkDecoratedVariable | undefined - set storagepropvar(storagepropvar: string | undefined) + @Watch({value:"storageLinkOnChange"}) get __backing_storagelinkvar(): (IStorageLinkDecoratedVariable | undefined) + set storagepropvar(storagepropvar: (string | undefined)) - get storagepropvar(): string | undefined - set __backing_storagepropvar(__backing_storagepropvar: StoragePropDecoratedVariable | undefined) + get storagepropvar(): (string | undefined) + @Watch({value:"storagePropOnChange"}) set __backing_storagepropvar(__backing_storagepropvar: (IStoragePropRefDecoratedVariable | undefined)) - get __backing_storagepropvar(): StoragePropDecoratedVariable | undefined - set providevar(providevar: string | undefined) + @Watch({value:"storagePropOnChange"}) get __backing_storagepropvar(): (IStoragePropRefDecoratedVariable | undefined) + set objectlinkvar(objectlinkvar: (A | undefined)) - get providevar(): string | undefined - set __backing_providevar(__backing_providevar: ProvideDecoratedVariable | undefined) + get objectlinkvar(): (A | undefined) + @Watch({value:"objectLinkOnChange"}) set __backing_objectlinkvar(__backing_objectlinkvar: (IObjectLinkDecoratedVariable | undefined)) - get __backing_providevar(): ProvideDecoratedVariable | undefined + @Watch({value:"objectLinkOnChange"}) get __backing_objectlinkvar(): (IObjectLinkDecoratedVariable | undefined) + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + @Watch({value:"ProvideOnChange"}) set __backing_providevar(__backing_providevar: (IProvideDecoratedVariable | undefined)) + + @Watch({value:"ProvideOnChange"}) get __backing_providevar(): (IProvideDecoratedVariable | undefined) } -interface __Options_Child { +@Component() export interface __Options_Child { + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + @Watch({value:"ConsumeOnChange"}) set __backing_providevar(__backing_providevar: (IConsumeDecoratedVariable | undefined)) + + @Watch({value:"ConsumeOnChange"}) get __backing_providevar(): (IConsumeDecoratedVariable | undefined) } @@ -287,7 +375,6 @@ class __EntryWrapper extends EntryPoint { public constructor() {} } - `; function testWatchTransformer(this: PluginTestContext): void { @@ -296,9 +383,9 @@ function testWatchTransformer(this: PluginTestContext): void { pluginTester.run( 'test basic watch transform', - [watchTransform, uiNoRecheck], + [watchTransform, uiNoRecheck, recheck], { - checked: [testWatchTransformer], + 'checked:ui-no-recheck': [testWatchTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..570b3cf1af5611e5e1600cef15109170acbc0adb --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts @@ -0,0 +1,163 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const DOUBLE_DOLLAR_DIR_PATH: string = 'double-dollar'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, DOUBLE_DOLLAR_DIR_PATH, 'double-dollar-griditem.ets'), +]; + +const pluginTester = new PluginTester('test griditem bindable capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'double-dollar-griditem', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { GridItemAttribute as GridItemAttribute } from "arkui.component.gridItem"; + +import { Bindable as Bindable } from "arkui.component.common"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Entry as Entry, Column as Column, Component as Component, $$ as $$, Grid as Grid, GridItem as GridItem } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +let c: boolean; + +function main() {} + +c = false; + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../double-dollar/double-dollar-griditem", + pageFullPath: "test/demo/mock/double-dollar/double-dollar-griditem", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_boo = STATE_MGMT_FACTORY.makeState(this, "boo", ((({let gensym___9142460 = initializers; + (((gensym___9142460) == (null)) ? undefined : gensym___9142460.boo)})) ?? (true))); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_boo?: IStateDecoratedVariable; + + public get boo(): boolean { + return this.__backing_boo!.get(); + } + + public set boo(value: boolean) { + this.__backing_boo!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Grid(undefined, undefined, undefined, @memo() (() => { + GridItem(@memo() ((instance: GridItemAttribute): void => { + instance.selected(({ + value: this.boo, + onChange: ((value: boolean) => { + this.boo = value; + }), + } as Bindable)); + return; + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); + })); + GridItem(@memo() ((instance: GridItemAttribute): void => { + instance.selected(({ + value: c, + onChange: ((value: boolean) => { + c = value; + }), + } as Bindable)); + return; + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); + })); + })); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set boo(boo: (boolean | undefined)) + + get boo(): (boolean | undefined) + set __backing_boo(__backing_boo: (IStateDecoratedVariable | undefined)) + + get __backing_boo(): (IStateDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test griditem bindable capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..2688f3be50a0aea1e0e0283a524bf46815e707e3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-toggle.test.ts @@ -0,0 +1,164 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const DOUBLE_DOLLAR_DIR_PATH: string = 'double-dollar'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, DOUBLE_DOLLAR_DIR_PATH, 'double-dollar-toggle.ets'), +]; + +const pluginTester = new PluginTester('test toggle bindable capability', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsedTrans', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { Bindable as Bindable } from "arkui.component.common"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Text as Text, Column as Column, Component as Component, $$ as $$, Toggle as Toggle, ToggleType as ToggleType } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +let c: Array; + +function main() {} + +c = [true, false, true]; + +class BooleanClass { + public isOn: boolean = true; + + public constructor() {} + +} + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_boo = STATE_MGMT_FACTORY.makeState>(this, "boo", ((({let gensym___9142460 = initializers; + (((gensym___9142460) == (null)) ? undefined : gensym___9142460.boo)})) ?? ([true, false, true]))); + this.__backing_booClass = STATE_MGMT_FACTORY.makeState(this, "booClass", ((({let gensym___145381365 = initializers; + (((gensym___145381365) == (null)) ? undefined : gensym___145381365.booClass)})) ?? (new BooleanClass()))); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_boo?: IStateDecoratedVariable>; + + public get boo(): Array { + return this.__backing_boo!.get(); + } + + public set boo(value: Array) { + this.__backing_boo!.set(value); + } + + private __backing_booClass?: IStateDecoratedVariable; + + public get booClass(): BooleanClass { + return this.__backing_booClass!.get(); + } + + public set booClass(value: BooleanClass) { + this.__backing_booClass!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: this.boo[0], + onChange: ((value: boolean) => { + this.boo[0] = value; + }), + } as Bindable), + }, undefined); + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: this.booClass.isOn, + onChange: ((value: boolean) => { + this.booClass.isOn = value; + }), + } as Bindable), + }, undefined); + Toggle(undefined, { + type: ToggleType.Checkbox, + isOn: ({ + value: c[1], + onChange: ((value: boolean) => { + c[1] = value; + }), + } as Bindable), + }, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set boo(boo: (Array | undefined)) + + get boo(): (Array | undefined) + set __backing_boo(__backing_boo: (IStateDecoratedVariable> | undefined)) + + get __backing_boo(): (IStateDecoratedVariable> | undefined) + set booClass(booClass: (BooleanClass | undefined)) + + get booClass(): (BooleanClass | undefined) + set __backing_booClass(__backing_booClass: (IStateDecoratedVariable | undefined)) + + get __backing_booClass(): (IStateDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test toggle bindable capability', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..98c87cc963629c031250d58de4dfa6f8763f0f36 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'entry-only.ets'), +]; + +const pluginTester = new PluginTester('test entry only', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry only', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +@Entry() @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() {} + +} + +@Entry() @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/entry-only", + pageFullPath: "test/demo/mock/entry/entry-only", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry only', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..23daf7f6fcc7c94608010ae6903dbeb634719710 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage-use-shared-storage-false.ets'), +]; + +const pluginTester = new PluginTester('test entry with storage and useSharedStorage false', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage",useSharedStorage:false}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, myStorage()); + } + +} + +@Entry({storage:"myStorage",useSharedStorage:false}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage-use-shared-storage-false", + pageFullPath: "test/demo/mock/entry/localstorage/storage-use-shared-storage-false", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with storage and useSharedStorage false', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..35a3172891994d681037ca83c06d9b8df290b106 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage-use-shared-storage-true.ets'), +]; + +const pluginTester = new PluginTester('test entry with storage and useSharedStorage true', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage",useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, myStorage()); + } + +} + +@Entry({storage:"myStorage",useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage-use-shared-storage-true", + pageFullPath: "test/demo/mock/entry/localstorage/storage-use-shared-storage-true", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with storage and useSharedStorage true', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..54549712e7ed07404a22bd4910c2bd572329d4c6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'storage.ets'), +]; + +const pluginTester = new PluginTester('test entry with only storage', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({storage:"myStorage"}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, myStorage()); + } + +} + +@Entry({storage:"myStorage"}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/storage", + pageFullPath: "test/demo/mock/entry/localstorage/storage", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryStorageTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only storage', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryStorageTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a75f6608b73e0834801c2409b427d82b2680f22 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'use-shared-storage-false.ets'), +]; + +const pluginTester = new PluginTester('test entry with only useSharedStorage false', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({useSharedStorage:false}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(false, undefined); + } + +} + +@Entry({useSharedStorage:false}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/use-shared-storage-false", + pageFullPath: "test/demo/mock/entry/localstorage/use-shared-storage-false", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only useSharedStorage false', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..523129df9e182a4fe4bafb0052fb379b12c7c973 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/localstorage'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'use-shared-storage-true.ets'), +]; + +const pluginTester = new PluginTester('test entry with only useSharedStorage true', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with storage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) +@Entry({useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, undefined); + } + +} + +@Entry({useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/localstorage/use-shared-storage-true", + pageFullPath: "test/demo/mock/entry/localstorage/use-shared-storage-true", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only useSharedStorage true', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..104404f1c3e3d2399327b3fce08032315f2e0fc9 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/route-name'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'route-name-storage-shared.ets'), +]; + +const pluginTester = new PluginTester('test entry with only routeName', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with routeName, storage, and useSharedStorage', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { LocalStorage as LocalStorage } from "@ohos.arkui.stateManagement"; + +const myStorage: (()=> LocalStorage) = (() => new LocalStorage()) + +@Entry({routeName:"MyPage",storage:"myStorage",useSharedStorage:true}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() { + super(true, myStorage()); + } + +} + +@Entry({routeName:"MyPage",storage:"myStorage",useSharedStorage:true}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("MyPage", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/route-name/route-name-storage-shared", + pageFullPath: "test/demo/mock/entry/route-name/route-name-storage-shared", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only routeName', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9d0ee82400a48ff4ad872d92c2cf321f02093927 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const FUNCTION_DIR_PATH: string = 'entry/route-name'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'route-name.ets'), +]; + +const pluginTester = new PluginTester('test entry with only routeName', buildConfig); + +const parsedTransform: Plugins = { + name: 'entry with routeName', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + + +@Entry({routeName:"MyPage"}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public build() {} + + public constructor() {} + +} + +@Entry({routeName:"MyPage"}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + MyStateSample(); + } + + public constructor() {} + +} + +__EntryWrapper.RegisterNamedRouter("MyPage", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../entry/route-name/route-name", + pageFullPath: "test/demo/mock/entry/route-name/route-name", + integratedHsp: "false", +} as NavInterface)) +`; + +function testEntryTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test entry with only routeName', + [parsedTransform, uiNoRecheck, recheck], + { + 'parsed': [testEntryTransformer], + }, + { + stopAfter: 'parsed', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..f72ccb65e1f5ab8466a7e4c6365bf41744db5e65 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts @@ -0,0 +1,130 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const IMPORT_DIR_PATH: string = 'imports'; +const IMPORT_UTILS_DIR_PATH: string = 'imports/utils'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_DIR_PATH, 'import-struct.ets'), + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_UTILS_DIR_PATH, 'simple-struct.ets') +]; + +const importParsed: Plugins = { + name: 'import-parsed', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test import transform', buildConfig); + +const expectedParsedScript: string = ` +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text } from "@ohos.arkui.component"; + +import { SimpleStruct as SimpleStruct } from "./utils/simple-struct"; + +@Component() final struct ImportStruct extends CustomComponent { + public build() { + SimpleStruct(); + SimpleStruct({ + message: "str1", + }); + SimpleStruct(){ + Text("a"); + }; + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +const expectedCheckedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text } from "@ohos.arkui.component"; + +import { SimpleStruct as SimpleStruct } from "./utils/simple-struct"; + +function main() {} + + + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build() { + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), undefined, undefined, undefined); + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), { + message: "str1", + }, undefined, undefined); + SimpleStruct._instantiateImpl(undefined, (() => { + return new SimpleStruct(); + }), undefined, undefined, @memo() (() => { + Text(undefined, "a", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testImportParsed(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testImportChecked(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test import struct from another file', + [importParsed, uiNoRecheck, recheck], + { + 'parsed:import-struct': [testImportParsed], + 'checked:ui-no-recheck:import-struct': [testImportChecked], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..62b772f7ad976b7d0b264043fa60cf0a61077d96 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts @@ -0,0 +1,239 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const IMPORT_DIR_PATH: string = 'imports'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, IMPORT_DIR_PATH, 'kit-import.ets'), +]; + +const importParsed: Plugins = { + name: 'import-parsed', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test import transform', buildConfig); + +const expectedParsedScript: string = ` +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; + +import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +import { Button as Button } from "arkui.component.button"; + +import hilog from "@ohos.hilog"; + +@Entry() @Component() final struct A extends CustomComponent implements PageLifeCycle { + @State() public a: string = "str"; + + @Prop() public b!: string; + + public build() { + Column(){ + Button("button").onClick(((e: ClickEvent) => {})); + Text("text").fontSize(20); + }; + } + + public constructor() {} + +} + +@Entry() @Component() export interface __Options_A { + a?: string; + @State() __backing_a?: string; + b?: string; + @Prop() __backing_b?: string; + +} + +class __EntryWrapper extends EntryPoint { + public entry(): void { + A(); + } + + public constructor() {} + +} +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../imports/kit-import", + pageFullPath: "test/demo/mock/imports/kit-import", + integratedHsp: "false", + } as NavInterface)) +`; + +const expectedCheckedScript: string = ` + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; + +import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { State as State } from "@ohos.arkui.stateManagement"; + +import { Button as Button } from "arkui.component.button"; + +import hilog from "@ohos.hilog"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../imports/kit-import", + pageFullPath: "test/demo/mock/imports/kit-import", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct A extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_a = STATE_MGMT_FACTORY.makeState(this, "a", ((({let gensym___94024326 = initializers; + (((gensym___94024326) == (null)) ? undefined : gensym___94024326.a)})) ?? ("str"))); + this.__backing_b = STATE_MGMT_FACTORY.makeProp(this, "b", (initializers!.b as string)); + } + + public __updateStruct(initializers: (__Options_A | undefined)): void { + if (((({let gensym___81454501 = initializers; + (((gensym___81454501) == (null)) ? undefined : gensym___81454501.b)})) !== (undefined))) { + this.__backing_b!.update((initializers!.b as string)); + } + } + + private __backing_a?: IStateDecoratedVariable; + + public get a(): string { + return this.__backing_a!.get(); + } + + public set a(value: string) { + this.__backing_a!.set(value); + } + + private __backing_b?: IPropDecoratedVariable; + + public get b(): string { + return this.__backing_b!.get(); + } + + public set b(value: string) { + this.__backing_b!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => {})); + return; + }), "button", undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(20); + return; + }), "text", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_A { + set a(a: (string | undefined)) + + get a(): (string | undefined) + set __backing_a(__backing_a: (IStateDecoratedVariable | undefined)) + + get __backing_a(): (IStateDecoratedVariable | undefined) + set b(b: (string | undefined)) + + get b(): (string | undefined) + set __backing_b(__backing_b: (IPropDecoratedVariable | undefined)) + + get __backing_b(): (IPropDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + A._instantiateImpl(undefined, (() => { + return new A(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testImportParsed(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +function testImportChecked(this: PluginTestContext): void { + expect(parseDumpSrc(this?.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedCheckedScript)); +} + +pluginTester.run( + 'test imports from different sources', + [importParsed, uiNoRecheck, recheck], + { + parsed: [testImportParsed], + 'checked:ui-no-recheck': [testImportChecked], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c8334a9c9c74fbcfc4c7c32757fdbb9435464c89 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/builder-in-generic.test.ts @@ -0,0 +1,303 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'builder-in-generic.ets'), +]; + +const pluginTester = new PluginTester('test builder with generic builder type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +@memo() let globalBuilder: @Builder() ((value: string, size: number)=> void); +let builderArr: Array<@Builder() ((value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} +@memo() function YourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size).fontColor(Color.Pink); + return; + }), value, undefined, undefined); +} +globalBuilder = MyBuilder; +builderArr = [MyBuilder, YourBuilder]; +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build() { + Row(@memo() ((instance: RowAttribute): void => { + instance.height(\"100%\"); + return; + }), undefined, @memo() (() => { + globalBuilder(this.message, 50); + ForEach(((): Array<@Builder() ((value: string, size: number)=> void)> => { + return builderArr; + }), ((@memo() item: @Builder() ((value: string, size: number)=> void)) => { + item(\"Hello World\", 30); + })); + })); + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + public constructor() {} +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +@memo() let globalBuilder: @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); +let builderArr: Array<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +@memo() function YourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value).fontColor(Color.Pink); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +globalBuilder = MyBuilder; +builderArr = [MyBuilder, YourBuilder]; +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: RowAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.height(\"100%\"); + { + __memo_scope.recache(); + return; + } + }), undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder(__memo_context, ((__memo_id) + ()), this.message, 50); + ForEach(__memo_context, ((__memo_id) + ()), ((): Array<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)> => { + return builderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() item: @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value(__memo_context, ((__memo_id) + ()), \"Hello World\", 30); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Index._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test builder with generic builder type', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1e714dba965928b30881d16be60af5ca6933bf4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'init-with-builder.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder init with @Builder function', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column } from "@kit.ArkUI"; + +let globalBuilder: WrappedBuilder; + +function main() {} + +@memo() function myBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +globalBuilder = wrapBuilder(myBuilder); + +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + globalBuilder.builder("hello", 50); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column } from "@kit.ArkUI"; + +let globalBuilder: WrappedBuilder; + +function main() {} + +@memo() function myBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (52041161)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (175145513)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (47330804)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +globalBuilder = wrapBuilder(myBuilder); + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (172572715)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (213104625)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (211301233)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder.builder(__memo_context, ((__memo_id) + (137225318)), "hello", 50); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder init with @Builder function', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..905a8dad5bc0412a5d4eadef1734cfc68c4d272d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-generic.test.ts @@ -0,0 +1,311 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-in-generic.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder with generic builder type', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed, +}; + +const expectedUIScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +let globalBuilder: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +let builderArr: Array void)>>; +let wrappedBuilder1: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +let wrappedBuilder2: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} +@memo() function YourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size).fontColor(Color.Pink); + return; + }), value, undefined, undefined); +} +globalBuilder = wrapBuilder(MyBuilder); +builderArr = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)]; +wrappedBuilder1 = wrapBuilder<@Builder() ((value: string, size: number)=> void)>(MyBuilder); +wrappedBuilder2 = new WrappedBuilder<@Builder() ((value: string, size: number)=> void)>(MyBuilder); +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/wrap-builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/wrap-builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build() { + Row(@memo() ((instance: RowAttribute): void => { + instance.height(\"100%\"); + return; + }), undefined, @memo() (() => { + globalBuilder.builder(this.message, 50); + ForEach(((): Array void)>> => { + return builderArr; + }), ((item: WrappedBuilder<@Builder() ((value: string, size: number)=> void)>) => { + item.builder(\"Hello World\", 30); + })); + })); + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + public constructor() {} +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from \"arkui.stateManagement.decorator\"; +import { IStateDecoratedVariable as IStateDecoratedVariable } from \"arkui.stateManagement.decorator\"; +import { RowAttribute as RowAttribute } from \"arkui.component.row\"; +import { memo as memo } from \"arkui.stateManagement.runtime\"; +import { TextAttribute as TextAttribute } from \"arkui.component.text\"; +import { NavInterface as NavInterface } from \"arkui.UserView\"; +import { PageLifeCycle as PageLifeCycle } from \"arkui.component.customComponent\"; +import { EntryPoint as EntryPoint } from \"arkui.UserView\"; +import { CustomComponent as CustomComponent } from \"arkui.component.customComponent\"; +import { Builder as Builder, Text as Text, Color as Color, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Entry as Entry, Component as Component, Row as Row, ForEach as ForEach } from \"@ohos.arkui.component\"; +import { State as State } from \"@ohos.arkui.stateManagement\"; +let globalBuilder: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +let builderArr: Array void)>>; +let wrappedBuilder1: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +let wrappedBuilder2: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>; +function main() {} +@memo() function MyBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +@memo() function YourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value).fontColor(Color.Pink); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} +globalBuilder = wrapBuilder(MyBuilder); +builderArr = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)]; +wrappedBuilder1 = wrapBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>(MyBuilder); +wrappedBuilder2 = new WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>(MyBuilder); +__EntryWrapper.RegisterNamedRouter(\"\", new __EntryWrapper(), ({ + bundleName: \"com.example.mock\", + moduleName: \"entry\", + pagePath: \"../../../wrap-builder/wrap-builder-in-generic\", + pageFullPath: \"test/demo/mock/wrap-builder/wrap-builder-in-generic\", + integratedHsp: \"false\", +} as NavInterface)); +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_message = STATE_MGMT_FACTORY.makeState(this, \"message\", ((({let gensym___ = initializers; + (((gensym___) == (null)) ? undefined : gensym___.message)})) ?? (\"Hello World\"))); + } + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + private __backing_message?: IStateDecoratedVariable; + public get message(): string { + return this.__backing_message!.get(); + } + public set message(value: string) { + this.__backing_message!.set(value); + } + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Row(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: RowAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.height(\"100%\"); + { + __memo_scope.recache(); + return; + } + }), undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + globalBuilder.builder(__memo_context, ((__memo_id) + ()), this.message, 50); + ForEach(__memo_context, ((__memo_id) + ()), ((): Array void)>> => { + return builderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: WrappedBuilder<@Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void)>) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value.builder(__memo_context, ((__memo_id) + ()), \"Hello World\", 30); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +@Entry({useSharedStorage:false,storage:\"\",routeName:\"\"}) @Component() export interface __Options_Index { + set message(message: (string | undefined)) + get message(): (string | undefined) + set __backing_message(__backing_message: (IStateDecoratedVariable | undefined)) + get __backing_message(): (IStateDecoratedVariable | undefined) +} +class __EntryWrapper extends EntryPoint { + @memo() public entry(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Index._instantiateImpl(__memo_context, ((__memo_id) + ()), undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder with generic builder type', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..30038d9d3635e08418e7185e541dbb02a6d48865 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts @@ -0,0 +1,255 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-in-ui.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder used in UI', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +const globalBuilderArr: Array> = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +function main() {} + + +@memo() function myBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +@memo() function yourBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + +globalBuilderArr = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public testBuilder() { + ForEach(((): Array> => { + return globalBuilderArr; + }), ((item: WrappedBuilder) => { + item.builder("hello world", 39); + })); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + this.testBuilder(); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder, Builder as Builder, Column as Column, ForEach as ForEach } from "@kit.ArkUI"; + +const globalBuilderArr: Array> = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +function main() {} + + +@memo() function myBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (52041161)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (175145513)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (47330804)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +@memo() function yourBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number) { + const __memo_scope = __memo_context.scope(((__memo_id) + (151467670)), 2); + const __memo_parameter_value = __memo_scope.param(0, value), __memo_parameter_size = __memo_scope.param(1, size); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (211301233)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: TextAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (137225318)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.fontSize(__memo_parameter_size.value); + { + __memo_scope.recache(); + return; + } + }), __memo_parameter_value.value, undefined, undefined); + { + __memo_scope.recache(); + return; + } +} + +globalBuilderArr = [wrapBuilder(myBuilder), wrapBuilder(yourBuilder)]; + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string, size: number)=> void); + +@Component() final struct ImportStruct extends CustomComponent { + public __initializeStruct(initializers: (__Options_ImportStruct | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_ImportStruct | undefined)): void {} + + @memo() public testBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (194881372)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + ForEach(__memo_context, ((__memo_id) + (218979098)), ((): Array> => { + return globalBuilderArr; + }), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: WrappedBuilder) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_item.value.builder(__memo_context, ((__memo_id) + (46726221)), "hello world", 39); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (142886843)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (54078781)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213687742)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + this.testBuilder(__memo_context, ((__memo_id) + (192802443))); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_ImportStruct { + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder in UI', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab0ee2fdf46d859c8475522f9ca9df932792a20d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts @@ -0,0 +1,390 @@ +/* + * Copyright (c) 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 path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { uiNoRecheck, recheck, memoNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const WRAP_BUILDER_DIR_PATH: string = 'wrap-builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WRAP_BUILDER_DIR_PATH, 'wrap-builder-with-lambda.ets'), +]; + +const pluginTester = new PluginTester('test wrap builder with lambda', buildConfig); + +const parsedTransform: Plugins = { + name: 'parsed transform', + parsed: uiTransform().parsed +}; + +const expectedUIScript: string = ` + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Observed as Observed, Builder as Builder, Entry as Entry, Component as Component, State as State } from "@kit.ArkUI"; + +import { WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from "@kit.ArkUI"; + +import { Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent } from "@kit.ArkUI"; + +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +function main() {} + + +@memo() function overBuilder(param: (()=> Tmp)) { + Column(undefined, undefined, @memo() (() => { + Text(undefined, \`wrapBuildervalue:\${param().paramA2}\`, undefined, undefined); + })); +} + +wBuilder = wrapBuilder(overBuilder); + +@Observed() class Tmp implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"paramA2"}) private __backing_paramA2: string = "hello"; + + public constructor() {} + + public get paramA2(): string { + this.conditionalAddRef(this.__meta); + return this.__backing_paramA2; + } + + public set paramA2(newValue: string) { + if (((this.__backing_paramA2) !== (newValue))) { + this.__backing_paramA2 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("paramA2"); + } + } + +} + +@memo() type MyBuilderFuncType = @Builder() ((param: (()=> Tmp))=> void); + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_label = STATE_MGMT_FACTORY.makeState(this, "label", ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? (new Tmp()))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_label?: IStateDecoratedVariable; + + public get label(): Tmp { + return this.__backing_label!.get(); + } + + public set label(value: Tmp) { + this.__backing_label!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + wBuilder.builder((() => { + return { + paramA2: this.label.paramA2, + }; + })); + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.label.paramA2 = "ArkUI"; + })); + return; + }), "Click me", undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set label(label: (Tmp | undefined)) + + get label(): (Tmp | undefined) + set __backing_label(__backing_label: (IStateDecoratedVariable | undefined)) + + get __backing_label(): (IStateDecoratedVariable | undefined) + +} +`; + +function testUITransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedUIScript)); +} + +const expectedMemoScript: string = ` + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from "arkui.stateManagement.runtime"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; + +import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; + +import { IMutableStateMeta as IMutableStateMeta } from "arkui.stateManagement.decorator"; + +import { RenderIdType as RenderIdType } from "arkui.stateManagement.decorator"; + +import { WatchIdType as WatchIdType } from "arkui.stateManagement.decorator"; + +import { ISubscribedWatches as ISubscribedWatches } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Observed as Observed, Builder as Builder, Entry as Entry, Component as Component, State as State } from "@kit.ArkUI"; + +import { WrappedBuilder as WrappedBuilder, wrapBuilder as wrapBuilder } from "@kit.ArkUI"; + +import { Column as Column, Text as Text, Button as Button, ClickEvent as ClickEvent } from "@kit.ArkUI"; + +const wBuilder: WrappedBuilder = wrapBuilder(overBuilder); + +function main() {} + + +@memo() function overBuilder(__memo_context: __memo_context_type, __memo_id: __memo_id_type, param: (()=> Tmp)) { + const __memo_scope = __memo_context.scope(((__memo_id) + (133793681)), 1); + const __memo_parameter_param = __memo_scope.param(0, param); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (241913892)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (175145513)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Text(__memo_context, ((__memo_id) + (47330804)), undefined, \`wrapBuildervalue:\${__memo_parameter_param.value().paramA2}\`, undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +} + +wBuilder = wrapBuilder(overBuilder); + +@Observed() class Tmp implements IObservedObject, ISubscribedWatches { + @JSONStringifyIgnore() private subscribedWatches: ISubscribedWatches = STATE_MGMT_FACTORY.makeSubscribedWatches(); + + public addWatchSubscriber(watchId: WatchIdType): void { + this.subscribedWatches.addWatchSubscriber(watchId); + } + + public removeWatchSubscriber(watchId: WatchIdType): boolean { + return this.subscribedWatches.removeWatchSubscriber(watchId); + } + + public executeOnSubscribingWatches(propertyName: string): void { + this.subscribedWatches.executeOnSubscribingWatches(propertyName); + } + + @JSONStringifyIgnore() private ____V1RenderId: RenderIdType = 0; + + public setV1RenderId(renderId: RenderIdType): void { + this.____V1RenderId = renderId; + } + + protected conditionalAddRef(meta: IMutableStateMeta): void { + if (OBSERVE.shouldAddRef(this.____V1RenderId)) { + meta.addRef(); + } + } + + @JSONStringifyIgnore() private __meta: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + @JSONRename({newName:"paramA2"}) private __backing_paramA2: string = "hello"; + + public constructor() {} + + public get paramA2(): string { + this.conditionalAddRef(this.__meta); + return this.__backing_paramA2; + } + + public set paramA2(newValue: string) { + if (((this.__backing_paramA2) !== (newValue))) { + this.__backing_paramA2 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("paramA2"); + } + } + +} + +@memo() type MyBuilderFuncType = @Builder() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, param: (()=> Tmp))=> void); + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: (((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined)): void { + this.__backing_label = STATE_MGMT_FACTORY.makeState(this, "label", ((({let gensym___171896504 = initializers; + (((gensym___171896504) == (null)) ? undefined : gensym___171896504.label)})) ?? (new Tmp()))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + + private __backing_label?: IStateDecoratedVariable; + + public get label(): Tmp { + return this.__backing_label!.get(); + } + + public set label(value: Tmp) { + this.__backing_label!.set(value); + } + + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { + const __memo_scope = __memo_context.scope(((__memo_id) + (69406103)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + Column(__memo_context, ((__memo_id) + (218979098)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (76711614)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + wBuilder.builder(__memo_context, ((__memo_id) + (211301233)), (() => { + return { + paramA2: this.label.paramA2, + }; + })); + Button(__memo_context, ((__memo_id) + (46726221)), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, instance: ButtonAttribute): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + (213104625)), 1); + const __memo_parameter_instance = __memo_scope.param(0, instance); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + __memo_parameter_instance.value.onClick(((e: ClickEvent) => { + this.label.paramA2 = "ArkUI"; + })); + { + __memo_scope.recache(); + return; + } + }), "Click me", undefined, undefined); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set label(label: (Tmp | undefined)) + + get label(): (Tmp | undefined) + set __backing_label(__backing_label: (IStateDecoratedVariable | undefined)) + + get __backing_label(): (IStateDecoratedVariable | undefined) + +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedMemoScript)); +} + +pluginTester.run( + 'test wrap builder with lambda', + [parsedTransform, uiNoRecheck, memoNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testUITransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/utils/artkts-config.ts b/arkui-plugins/test/utils/artkts-config.ts index 3474c59a2f94e9c921fb48e0cfbf6b797cf8ebf2..d47091bd71dcb491d82c04c6a2e48e7e4f282869 100644 --- a/arkui-plugins/test/utils/artkts-config.ts +++ b/arkui-plugins/test/utils/artkts-config.ts @@ -21,19 +21,27 @@ import { changeFileExtension, ensurePathExists, getFileName, + getResourcePath, getRootPath, + MOCK_BUNDLE_NAME, + MOCK_DEP_ANALYZER_PATH, MOCK_ENTRY_DIR_PATH, MOCK_ENTRY_FILE_NAME, - MOCK_LOCAL_SDK_DIR_PATH, MOCK_OUTPUT_CACHE_PATH, MOCK_OUTPUT_DIR_PATH, MOCK_OUTPUT_FILE_NAME, + MOCK_PROJECT_ROOT_PATH, + MOCK_RAWFILE_DIR_PATH, + MOCK_RESOURCE_TABLE_FILE_NAME, PANDA_SDK_STDLIB_PATH, - RUNTIME_API_PATH, STDLIB_ESCOMPAT_PATH, STDLIB_PATH, STDLIB_STD_PATH, } from './path-config'; +import { ArkTSConfigContextCache } from './cache'; +import { BuildConfig, CompileFileInfo, DependentModule } from './shared-types'; +import { setUpSoPath } from './global'; +import { ProjectConfig } from '../../common/plugin-context'; export interface ArkTSConfigObject { compilerOptions: { @@ -45,15 +53,6 @@ export interface ArkTSConfigObject { }; } -export interface CompileFileInfo { - fileName: string; - filePath: string; - dependentFiles: string[]; - abcFilePath: string; - arktsConfigFile: string; - stdLibPath: string; -} - export interface ModuleInfo { isMainModule: boolean; packageName: string; @@ -65,45 +64,15 @@ export interface ModuleInfo { dependencies?: string[]; } -export interface DependentModule { - packageName: string; - moduleName: string; - moduleType: string; - modulePath: string; - sourceRoots: string[]; - entryFile: string; -} - -export type ModuleType = 'har' | string; // TODO: module type unclear - -export interface DependentModule { - packageName: string; - moduleName: string; - moduleType: ModuleType; - modulePath: string; - sourceRoots: string[]; - entryFile: string; -} - -export interface BuildConfig { - packageName: string; - compileFiles: string[]; - loaderOutPath: string; - cachePath: string; - pandaSdkPath: string; - buildSdkPath: string; - sourceRoots: string[]; - moduleRootPath: string; - dependentModuleList: DependentModule[]; -} - export interface ArktsConfigBuilder { buildConfig: BuildConfig; entryFiles: Set; + compileFiles: Map; outputDir: string; cacheDir: string; pandaSdkPath: string; - buildSdkPath: string; + apiPath: string; + kitsPath: string; packageName: string; sourceRoots: string[]; moduleRootPath: string; @@ -112,6 +81,12 @@ export interface ArktsConfigBuilder { mergedAbcFile: string; // logger: Logger; // TODO isDebug: boolean; + projectConfig: ProjectConfig; + + withBuildConfig(buildConfig: BuildConfig): this; + withProjectConfig(projectConfig: ProjectConfig): this; + + clear(): void; } function writeArkTSConfigFile( @@ -158,21 +133,6 @@ function getDependentModules(moduleInfo: ModuleInfo, moduleInfoMap: Map) { - const items = fs.readdirSync(currentDir); - for (const item of items) { - const itemPath = path.join(currentDir, item); - const stat = fs.statSync(itemPath); - - if (stat.isFile()) { - const basename = path.basename(item, '.d.ets'); - pathSection[basename] = [changeFileExtension(itemPath, '', '.d.ets')]; - } else if (stat.isDirectory()) { - traverse(itemPath, pathSection); - } - } -} - function traverseSDK(currentDir: string, pathSection: Record, prefix?: string) { const items = fs.readdirSync(currentDir); @@ -203,47 +163,131 @@ function mockBuildConfig(): BuildConfig { loaderOutPath: path.resolve(getRootPath(), MOCK_OUTPUT_DIR_PATH), cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), pandaSdkPath: global.PANDA_SDK_PATH, - buildSdkPath: global.API_PATH, + apiPath: global.API_PATH, + kitsPath: global.KIT_PATH, + depAnalyzerPath: path.resolve(global.PANDA_SDK_PATH, MOCK_DEP_ANALYZER_PATH), sourceRoots: [getRootPath()], moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), dependentModuleList: [], }; } +function mockProjectConfig(): ProjectConfig { + return { + bundleName: MOCK_BUNDLE_NAME, + moduleName: 'entry', + cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), + dependentModuleList: [], + appResource: path.resolve(getResourcePath(), MOCK_RESOURCE_TABLE_FILE_NAME), + rawFileResource: path.resolve(getResourcePath(), MOCK_RAWFILE_DIR_PATH), + buildLoaderJson: '', + hspResourcesMap: false, + compileHar: false, + byteCodeHar: false, + uiTransformOptimization: false, + resetBundleName: false, + allowEmptyBundleName: false, + moduleType: 'entry', + moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), + aceModuleJsonPath: '', + ignoreError: true, + projectPath: '', + projectRootPath: path.resolve(getRootPath(), MOCK_PROJECT_ROOT_PATH), + integratedHsp: false, + frameworkMode: undefined + }; +} + class MockArktsConfigBuilder implements ArktsConfigBuilder { - buildConfig: BuildConfig; - entryFiles: Set; - outputDir: string; - cacheDir: string; - pandaSdkPath: string; - buildSdkPath: string; - packageName: string; - sourceRoots: string[]; - moduleRootPath: string; - dependentModuleList: DependentModule[]; - moduleInfos: Map; - mergedAbcFile: string; + hashId: string; + buildConfig!: BuildConfig; + entryFiles!: Set; + compileFiles!: Map; + outputDir!: string; + cacheDir!: string; + pandaSdkPath!: string; + apiPath!: string; + kitsPath!: string; + packageName!: string; + sourceRoots!: string[]; + moduleRootPath!: string; + dependentModuleList!: DependentModule[]; + moduleInfos!: Map; + mergedAbcFile!: string; isDebug: boolean; + projectConfig: ProjectConfig; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig) { + this.hashId = hashId; - constructor(buildConfig?: BuildConfig) { const _buildConfig: BuildConfig = buildConfig ?? mockBuildConfig(); + this._setBuildConfig(_buildConfig); + + const _projectConfig: ProjectConfig = projectConfig ?? mockProjectConfig(); + this.projectConfig = _projectConfig; + + this.isDebug = true; + } + + private _setBuildConfig(buildConfig: BuildConfig): void { + const _buildConfig: BuildConfig = buildConfig; this.buildConfig = _buildConfig; this.entryFiles = new Set(_buildConfig.compileFiles as string[]); this.outputDir = _buildConfig.loaderOutPath as string; this.cacheDir = _buildConfig.cachePath as string; this.pandaSdkPath = path.resolve(_buildConfig.pandaSdkPath as string); - this.buildSdkPath = path.resolve(_buildConfig.buildSdkPath as string); + this.apiPath = path.resolve(_buildConfig.apiPath as string); + this.kitsPath = path.resolve(_buildConfig.kitsPath as string); this.packageName = _buildConfig.packageName as string; this.sourceRoots = _buildConfig.sourceRoots as string[]; this.moduleRootPath = path.resolve(_buildConfig.moduleRootPath as string); this.dependentModuleList = _buildConfig.dependentModuleList as DependentModule[]; - this.isDebug = true; + this.compileFiles = new Map(); this.moduleInfos = new Map(); this.mergedAbcFile = path.resolve(this.outputDir, MOCK_OUTPUT_FILE_NAME); + setUpSoPath(this.pandaSdkPath); this.generateModuleInfos(); this.generateArkTSConfigForModules(); + this.cacheArkTSConfig(); + } + + private cacheArkTSConfig(): void { + const mainModuleInfo: ModuleInfo = this.moduleInfos.get(this.moduleRootPath)!; + const arktsConfigFile: string = mainModuleInfo.arktsConfigFile; + const compileFiles: Map = this.compileFiles; + ArkTSConfigContextCache.getInstance().set(this.hashId, { arktsConfigFile, compileFiles }); + } + + private generateModuleInfosInEntryFile(file: string): void { + const _file = path.resolve(file); + for (const [modulePath, moduleInfo] of this.moduleInfos) { + if (!_file.startsWith(modulePath)) { + throw new Error('Entry File does not belong to any module in moduleInfos.'); + } + const filePathFromModuleRoot: string = path.relative(modulePath, _file); + const filePathInCache: string = path.join( + this.cacheDir, + this.hashId, + moduleInfo.packageName, + filePathFromModuleRoot + ); + const abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); + + const fileInfo: CompileFileInfo = { + fileName: getFileName(_file), + filePath: _file, + dependentFiles: [], + abcFilePath: abcFilePath, + arktsConfigFile: moduleInfo.arktsConfigFile, + stdLibPath: path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_PATH), + }; + moduleInfo.compileFileInfos.push(fileInfo); + if (!this.compileFiles.has(_file)) { + this.compileFiles.set(_file, fileInfo); + } + } } private generateModuleInfos(): void { @@ -256,7 +300,7 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { moduleRootPath: this.moduleRootPath, sourceRoots: this.sourceRoots, entryFile: '', - arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTS_CONFIG_FILE_PATH), + arktsConfigFile: path.resolve(this.cacheDir, this.hashId, this.packageName, ARKTS_CONFIG_FILE_PATH), compileFileInfos: [], }; this.moduleInfos.set(this.moduleRootPath, mainModuleInfo); @@ -270,36 +314,13 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { moduleRootPath: module.modulePath, sourceRoots: module.sourceRoots, entryFile: module.entryFile, - arktsConfigFile: path.resolve(this.cacheDir, module.packageName, ARKTS_CONFIG_FILE_PATH), + arktsConfigFile: path.resolve(this.cacheDir, this.hashId, module.packageName, ARKTS_CONFIG_FILE_PATH), compileFileInfos: [], }; this.moduleInfos.set(module.modulePath, moduleInfo); }); this.entryFiles.forEach((file: string) => { - const _file = path.resolve(file); - for (const [modulePath, moduleInfo] of this.moduleInfos) { - if (_file.startsWith(modulePath)) { - const filePathFromModuleRoot: string = path.relative(modulePath, _file); - const filePathInCache: string = path.join( - this.cacheDir, - moduleInfo.packageName, - filePathFromModuleRoot - ); - const abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - - const fileInfo: CompileFileInfo = { - fileName: getFileName(_file), - filePath: _file, - dependentFiles: [], - abcFilePath: abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - stdLibPath: path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_PATH), - }; - moduleInfo.compileFileInfos.push(fileInfo); - return; - } - } - throw new Error('Entry File does not belong to any module in moduleInfos.'); + this.generateModuleInfosInEntryFile(file); }); } @@ -307,7 +328,8 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { const pathSection: Record = {}; pathSection['std'] = [path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_STD_PATH)]; pathSection['escompat'] = [path.resolve(this.pandaSdkPath, PANDA_SDK_STDLIB_PATH, STDLIB_ESCOMPAT_PATH)]; - traverseSDK(this.buildSdkPath, pathSection); + traverseSDK(this.apiPath, pathSection); + traverseSDK(this.kitsPath, pathSection); this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleRootPath: string) => { pathSection[moduleInfo.packageName] = [path.resolve(moduleRootPath, moduleInfo.sourceRoots[0])]; @@ -329,6 +351,20 @@ class MockArktsConfigBuilder implements ArktsConfigBuilder { dependenciesSection.push(depModuleInfo.arktsConfigFile); }); } + + withBuildConfig(buildConfig: BuildConfig): this { + this._setBuildConfig(buildConfig); + return this; + } + + withProjectConfig(projectConfig: ProjectConfig): this { + this.projectConfig = projectConfig; + return this; + } + + clear(): void { + ArkTSConfigContextCache.getInstance().delete(this.hashId); + } } -export { mockBuildConfig, MockArktsConfigBuilder }; +export { mockBuildConfig, mockProjectConfig, MockArktsConfigBuilder }; diff --git a/arkui-plugins/test/utils/cache.ts b/arkui-plugins/test/utils/cache.ts index b24fcdf64e0385cafb3d75e3c0785c4be61242e9..06942d1c4635afeecd940407dcfeabf77ca12db7 100644 --- a/arkui-plugins/test/utils/cache.ts +++ b/arkui-plugins/test/utils/cache.ts @@ -13,21 +13,15 @@ * limitations under the License. */ +import { ArkTSConfigContext, FileDependencyContext, PluginTestContext } from './shared-types'; + class TesterCache { private cacheInfo: Map; - private static instance: TesterCache; - private constructor() { + constructor() { this.cacheInfo = new Map(); } - static getInstance(): TesterCache { - if (!this.instance) { - this.instance = new TesterCache(); - } - return this.instance; - } - public delete(key: string) { if (this.cacheInfo.has(key)) { this.cacheInfo.delete(key); @@ -56,4 +50,37 @@ class TesterCache { } } -export { TesterCache }; +class PluginTestContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new PluginTestContextCache(); + } + return this._instance; + } +} + +class ArkTSConfigContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new ArkTSConfigContextCache(); + } + return this._instance; + } +} + +class FileDependencyContextCache extends TesterCache { + private static _instance: TesterCache; + + static getInstance(): TesterCache { + if (!this._instance) { + this._instance = new FileDependencyContextCache(); + } + return this._instance; + } +} + +export { TesterCache, PluginTestContextCache, ArkTSConfigContextCache, FileDependencyContextCache }; diff --git a/arkui-plugins/test/utils/compile.ts b/arkui-plugins/test/utils/compile.ts index f893dae3b129643cef280ca4848cb5c4e70fe285..afa55219ab9435046acb914644789ece536583c9 100644 --- a/arkui-plugins/test/utils/compile.ts +++ b/arkui-plugins/test/utils/compile.ts @@ -13,48 +13,287 @@ * limitations under the License. */ -import { PluginDriver } from './plugin-driver'; -import { PluginContext, PluginExecutor } from '../../common/plugin-context'; -import { EtsglobalRemover } from '../../common/etsglobal-remover'; import * as arkts from '@koalaui/libarkts'; +import EventEmitter from 'events'; +import { + CompileStrategy, + JobInfo, + PluginTestContext, + ProcessEvent, + SingleProgramContext, + TraceOptions, +} from './shared-types'; +import { MockPluginDriver, stateName } from './plugin-driver'; +import { + createContextGenerateAbcForExternalSourceFiles, + createCacheContextFromFile, + destroyContext, + resetConfig, +} from './global'; +import { PluginDriver } from './plugin-driver'; +import { PluginState, PluginContext, PluginExecutor } from '../../common/plugin-context'; +import { concatObject } from './serializable'; -function restartCompilerUptoState(state: arkts.Es2pandaContextState, restart: boolean): boolean { - try { - const ast: arkts.EtsScript | undefined = arkts.EtsScript.fromContext(); - if (!ast) { - return false; - } +function insertPlugin(driver: PluginDriver, plugin: PluginExecutor | undefined): boolean { + const pluginContext: PluginContext = driver.getPluginContext(); + if (plugin) { + plugin.handler.apply(pluginContext); + } + return true; +} + +function collectPluginTextContextFromSourceProgram(program: arkts.Program, tracing: TraceOptions): PluginTestContext { + const pluginTestContext: PluginTestContext = {}; + const script: arkts.EtsScript = program.astNode; + pluginTestContext.scriptSnapshot = script.dumpSrc(); + return pluginTestContext; +} - if (restart) { - const srcText = new EtsglobalRemover().visitor(ast).dumpSrc(); - arkts.arktsGlobal.es2panda._DestroyContext(arkts.arktsGlobal.context); - arkts.arktsGlobal.compilerContext = arkts.Context.createFromString(srcText); +function collectPluginTextContextFromExternalSource( + externalSources: arkts.ExternalSource[], + tracing: TraceOptions, + matchSourceName: (name: string) => boolean, + useCache?: boolean +) { + let pluginTestContext: PluginTestContext = {}; + const filteredExternalSourceNames: string[] = [...tracing.externalSourceNames]; + const filteredExternalSources = externalSources.filter((source) => { + const name = source.getName(); + const sourceProgram: arkts.Program = source.programs[0]; + const shouldCollectByName = filteredExternalSourceNames.includes(name) || matchSourceName(name); + const shouldCollectByProgram = sourceProgram && (!useCache || sourceProgram.isASTLowered()); + return shouldCollectByName && shouldCollectByProgram; + }); + const declContexts: Record = {}; + filteredExternalSources.forEach((source) => { + const name: string = source.getName(); + const sourceProgram: arkts.Program = source.programs[0]; + if (matchSourceName(name)) { + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromSourceProgram(sourceProgram, tracing) + ); + } else { + const sourceTestContext: SingleProgramContext = {}; + const script: arkts.EtsScript = sourceProgram.astNode; + const scriptSnapshot = script.dumpSrc(); + sourceTestContext.scriptSnapshot = scriptSnapshot; + declContexts[name] = sourceTestContext; } + }); + pluginTestContext.declContexts = declContexts; + return pluginTestContext; +} - arkts.proceedToState(state); - return true; +function collectPluginTestContext( + context: arkts.Context, + compileStrategy: CompileStrategy, + tracing: TraceOptions, + matchSourceName: (name: string) => boolean +): PluginTestContext { + const useCache: boolean = compileStrategy !== CompileStrategy.ABC_WTIH_EXTERNAL; + const canCollectSource: boolean = !useCache || compileStrategy === CompileStrategy.ABC; + const canCollectExternal: boolean = !useCache || compileStrategy === CompileStrategy.EXTERNAL; + let pluginTestContext: PluginTestContext = {}; + try { + const program: arkts.Program = arkts.getOrUpdateGlobalContext(context.peer).program; + // TODO: add error/warning handling after plugin + if (canCollectSource) { + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromSourceProgram(program, tracing) + ); + } + if (canCollectExternal) { + const externalSources: arkts.ExternalSource[] = program.externalSources; + pluginTestContext = concatObject( + pluginTestContext, + collectPluginTextContextFromExternalSource(externalSources, tracing, matchSourceName, useCache) + ); + } } catch (e) { - return false; + // Do nothing + } finally { + return pluginTestContext; } } -function insertPlugin( - driver: PluginDriver, - plugin: PluginExecutor | undefined, - state: arkts.Es2pandaContextState +function buildMatchNameFunc(prefix: string, suffix: string): (name: string) => boolean { + return (name: string): boolean => { + return name.startsWith(`${prefix}.`) && name.endsWith(`.${suffix}`); + }; +} + +/** + * @param emitter event emitter. + * @param jobInfo job info. + * @param state the current state. + * @param context context for the single file. + * @param stopAfter state that should stop after running plugins. + * @returns boolean indicates whether should proceed to the next state. + */ +function runPluginsAtState( + emitter: EventEmitter, + jobInfo: JobInfo, + state: arkts.Es2pandaContextState, + context: arkts.Context, + tracing: TraceOptions, + stopAfter?: PluginState ): boolean { - arkts.proceedToState(state); - const pluginContext: PluginContext = driver.getPluginContext(); - const ast: arkts.EtsScript | undefined = arkts.EtsScript.fromContext(); + const stateStr = stateName(state); + const plugins = MockPluginDriver.getInstance().getSortedPlugins(state); + const packageName = jobInfo.buildConfig!.packageName; + const fileName = jobInfo.compileFileInfo!.fileName; + const matchSourceName = buildMatchNameFunc(packageName, fileName); + const compileStrategy = jobInfo.isCompileAbc; + if (plugins && plugins.length > 0) { + plugins.forEach((plugin) => { + insertPlugin(MockPluginDriver.getInstance(), plugin); + const pluginName: string = plugin.name; + const pluginStateId: `${PluginState}:${string}` = `${stateStr}:${pluginName}`; + const pluginTestContext = collectPluginTestContext(context, compileStrategy, tracing, matchSourceName); + emitter.emit('TASK_COLLECT', { + jobId: jobInfo.id, + pluginStateId, + pluginTestContext: pluginTestContext, + fileName, + }); + }); + } + const pluginStateId: PluginState = `${stateStr}`; + const pluginTestContext = collectPluginTestContext(context, compileStrategy, tracing, matchSourceName); + emitter.emit('TASK_COLLECT', { + jobId: jobInfo.id, + pluginStateId, + pluginTestContext: pluginTestContext, + fileName, + }); + return !!stopAfter && stopAfter === stateStr; +} + +function createContextForAbcCompilation(jobInfo: JobInfo): arkts.Context { + const fileInfo = jobInfo.compileFileInfo!; + const globalContextPtr = jobInfo.globalContextPtr!; + const ets2pandaCmd = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + fileInfo.arktsConfigFile, + '--output', + fileInfo.abcFilePath, + ]; + ets2pandaCmd.push(fileInfo.filePath); + const config = resetConfig(ets2pandaCmd); + const context = createCacheContextFromFile(config, fileInfo.filePath, globalContextPtr, false); + return context; +} + +function createContextForExternalCompilation(jobInfo: JobInfo): arkts.Context { + const fileInfo = jobInfo.compileFileInfo!; + const globalContextPtr = jobInfo.globalContextPtr!; + const ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', fileInfo.arktsConfigFile]; + ets2pandaCmd.push(fileInfo.filePath); + const config = resetConfig(ets2pandaCmd); + const context = createCacheContextFromFile(config, fileInfo.filePath, globalContextPtr, true); + return context; +} - if (!ast) { - return false; +function compileAbcWithExternal(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextGenerateAbcForExternalSourceFiles(jobInfo.filePaths!); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + const stopAfter = jobInfo.stopAfter!; + let shouldStop = false; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); + return; } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); + return; + } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); + emitter.emit('TASK_FINISH', { jobId: 'compile-abc-with-external' }); +} - if (plugin) { - plugin.handler.apply(pluginContext); +function compileAbc(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextForAbcCompilation(jobInfo); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + const stopAfter = jobInfo.stopAfter!; + let shouldStop = false; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + return; } - return true; + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + shouldStop = runPluginsAtState( + emitter, + jobInfo, + arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + context, + tracing, + stopAfter + ); + if (shouldStop) { + destroyContext(context); + MockPluginDriver.getInstance().clear(); + return; + } + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); +} + +function compileExternalProgram(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { + MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); + MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); + const context = createContextForExternalCompilation(jobInfo); + MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context.peer); + runPluginsAtState(emitter, jobInfo, arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, context, tracing); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context.peer); + runPluginsAtState(emitter, jobInfo, arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, context, tracing); + arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, context.peer); + destroyContext(context); + MockPluginDriver.getInstance().clear(); } -export { restartCompilerUptoState, insertPlugin }; +export { compileAbcWithExternal, compileAbc, compileExternalProgram }; diff --git a/arkui-plugins/test/utils/global.ts b/arkui-plugins/test/utils/global.ts index cbccd4afa9ac1174702cc789f8ddf6f842dece85..424d4512edbc6ec0b3f3cdef7ce8ab215b795b65 100644 --- a/arkui-plugins/test/utils/global.ts +++ b/arkui-plugins/test/utils/global.ts @@ -13,11 +13,15 @@ * limitations under the License. */ -import * as fs from 'fs'; -import { CompileFileInfo } from './artkts-config'; +import fs from 'fs' import * as arkts from '@koalaui/libarkts'; +import { CompileFileInfo } from './shared-types'; -function initGlobal(fileInfo: CompileFileInfo, isDebug: boolean = true): void { +function createGlobalConfig( + fileInfo: CompileFileInfo, + isDebug: boolean = true, + isUseCache: boolean = true +): arkts.Config { const config = [ '_', '--extension', @@ -31,13 +35,44 @@ function initGlobal(fileInfo: CompileFileInfo, isDebug: boolean = true): void { if (isDebug) { config.push('--debug-info'); } + if (!isUseCache) { + config.push('--simultaneous'); + } config.push(fileInfo.filePath); + if (isUseCache) { + arkts.MemInitialize(); + } arkts.arktsGlobal.filePath = fileInfo.filePath; - resetConfig(config); + return resetConfig(config); +} + +function destroyGlobalConfig(config: arkts.Config, isUseCache: boolean = true): void { + destroyConfig(config); + if (isUseCache) { + arkts.MemFinalize(); + } +} + +function createGlobalContextPtr(config: arkts.Config, files: string[]): number { + return arkts.CreateGlobalContext(config.peer, files, files.length, false); +} - const source: string = fs.readFileSync(fileInfo.filePath).toString(); - resetContext(source); +function destroyGlobalContextPtr(globalContextPtr: number): void { + arkts.DestroyGlobalContext(globalContextPtr); +} + +function createCacheContextFromFile( + config: arkts.Config, + filePath: string, + globalContextPtr: number, + isExternal: boolean +): arkts.Context { + return arkts.Context.createCacheContextFromFile(config.peer, filePath, globalContextPtr, isExternal); +} + +function createContextGenerateAbcForExternalSourceFiles(filePaths: string[]): arkts.Context { + return arkts.Context.createContextGenerateAbcForExternalSourceFiles(filePaths); } function resetContext(source: string): void { @@ -51,48 +86,49 @@ function resetContext(source: string): void { } } -function resetConfig(cmd: string[]): void { +function resetConfig(cmd: string[]): arkts.Config { try { arkts.arktsGlobal.config; - destroyConfig(); + destroyConfig(arkts.arktsGlobal.config); } catch (e) { // Do nothing } finally { - const arkTSConfig: arkts.Config = arkts.Config.create(cmd); - arkts.arktsGlobal.config = arkTSConfig.peer; + const config = arkts.Config.create(cmd); + arkts.arktsGlobal.config = config.peer; + return config; } } -function destroyContext(): void { +function destroyContext(context: arkts.Context): void { try { - arkts.arktsGlobal.clearContext(); + arkts.arktsGlobal.es2panda._DestroyContext(context.peer); } catch (e) { // Do nothing } } -function destroyConfig(): void { +function destroyConfig(config: arkts.Config): void { try { - arkts.destroyConfig(arkts.arktsGlobal.config); + arkts.destroyConfig(config); } catch (e) { // Do nothing } } -function canProceedToState(state: arkts.Es2pandaContextState): boolean { - const stateToSkip: arkts.Es2pandaContextState[] = [ - arkts.Es2pandaContextState.ES2PANDA_STATE_SCOPE_INITED, - arkts.Es2pandaContextState.ES2PANDA_STATE_BOUND, - arkts.Es2pandaContextState.ES2PANDA_STATE_LOWERED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ASM_GENERATED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, - ]; - if (state in stateToSkip) { - return false; - } - - const currState = arkts.arktsGlobal.es2panda._ContextState(arkts.arktsGlobal.context); - return currState < state; +function setUpSoPath(pandaSdkPath: string): void { + arkts.arktsGlobal.es2panda._SetUpSoPath(pandaSdkPath); } -export { initGlobal, resetContext, resetConfig, destroyContext, destroyConfig, canProceedToState }; +export { + createGlobalConfig, + destroyGlobalConfig, + createGlobalContextPtr, + destroyGlobalContextPtr, + createCacheContextFromFile, + createContextGenerateAbcForExternalSourceFiles, + resetContext, + resetConfig, + destroyContext, + destroyConfig, + setUpSoPath, +}; diff --git a/arkui-plugins/test/utils/hash-generator.ts b/arkui-plugins/test/utils/hash-generator.ts new file mode 100644 index 0000000000000000000000000000000000000000..09e0b6d45678129575b7cbf005d65ef87163bbbb --- /dev/null +++ b/arkui-plugins/test/utils/hash-generator.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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 { randomBytes } from 'crypto'; +import { getCommonPath } from '../../path'; +const common = require(getCommonPath()); +const UniqueId = common.UniqueId; + +export class HashGenerator { + static instance: HashGenerator; + + private constructor() {} + + static getInstance(): HashGenerator { + if (!this.instance) { + this.instance = new HashGenerator(); + } + + return this.instance; + } + + dynamicSha1Id(id: string, length: number = 7): string { + const uniqId = new UniqueId(); + uniqId.addString('hashId'); + uniqId.addString(id); + uniqId.addString(randomBytes(length).toString('hex')); + return uniqId.compute().substring(0, length); + } + + staticSha1Id(id: string, length: number = 7): string { + const uniqId = new UniqueId(); + uniqId.addString('hashId'); + uniqId.addString(id); + return uniqId.compute().substring(0, length); + } +} diff --git a/arkui-plugins/test/utils/path-config.ts b/arkui-plugins/test/utils/path-config.ts index 608df6deec29a6afd1087b59ef8928a2949c0366..bb70824989c4387fdac8e5e32d586787a7299cdc 100644 --- a/arkui-plugins/test/utils/path-config.ts +++ b/arkui-plugins/test/utils/path-config.ts @@ -17,23 +17,32 @@ import * as fs from 'fs'; import * as path from 'path'; export const ARKTS_CONFIG_FILE_PATH: string = 'arktsconfig.json'; -export const PANDA_SDK_PATH: string = 'node_modules/@panda/sdk'; export const PANDA_SDK_STDLIB_PATH: string = 'lib'; export const STDLIB_PATH: string = 'stdlib'; export const STDLIB_STD_PATH: string = 'stdlib/std'; export const STDLIB_ESCOMPAT_PATH: string = 'stdlib/escompat'; -export const RUNTIME_API_PATH: string = 'demo/runtime-api'; +export const MOCK_PROJECT_ROOT_PATH: string = 'demo'; export const MOCK_ENTRY_DIR_PATH: string = 'demo/mock'; export const MOCK_ENTRY_FILE_NAME: string = 'entry.ets'; export const MOCK_OUTPUT_CACHE_PATH: string = 'generated/cache'; export const MOCK_OUTPUT_DIR_PATH: string = 'generated/abc'; export const MOCK_OUTPUT_FILE_NAME: string = 'entry.abc'; -export const MOCK_LOCAL_SDK_DIR_PATH: string = 'local'; +export const MOCK_DEP_ANALYZER_PATH: string = 'bin/dependency_analyzer'; +export const MOCK_FILE_DEP_FILE_NAME: string = 'file_dependencies.json'; +export const MOCK_DEP_INPUT_FILE_NAME: string = 'depInput.txt'; +export const MOCK_RESOURCE_TABLE_FILE_NAME: string = 'ResourceTable.txt'; +export const MOCK_BUNDLE_NAME: string = 'com.example.mock'; +export const MOCK_RAWFILE_DIR_PATH: string = 'rawfile'; export const ETS_SUFFIX: string = '.ets'; export const ABC_SUFFIX: string = '.abc'; +export const DECL_ETS_SUFFIX: string = '.d.ets'; function getRootPath(): string { - return path.resolve(__dirname, '..'); + return path.resolve(__dirname, '..', '..', 'test'); +} + +function getResourcePath(): string { + return path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'resource'); } function changeFileExtension(file: string, targetExt: string, originExt = ''): string { @@ -61,4 +70,4 @@ function ensurePathExists(filePath: string): void { } } -export { getRootPath, changeFileExtension, getFileName, ensurePathExists }; +export { getRootPath, getResourcePath, changeFileExtension, getFileName, ensurePathExists }; diff --git a/arkui-plugins/test/utils/plugin-driver.ts b/arkui-plugins/test/utils/plugin-driver.ts index 46ce7f562cc13bd90b3afd90b667cd2274c1e59b..ef64a91bf0bc2c6f3e5774c1346bae9921441db5 100644 --- a/arkui-plugins/test/utils/plugin-driver.ts +++ b/arkui-plugins/test/utils/plugin-driver.ts @@ -15,12 +15,15 @@ import { isNumber } from './safe-types'; import { Plugins, PluginContext, PluginHandler, PluginState, PluginExecutor } from '../../common/plugin-context'; +import { PluginStateId } from './shared-types'; import * as arkts from '@koalaui/libarkts'; export interface PluginDriver { initPlugins(plugins: Plugins[]): void; getSortedPlugins(state: arkts.Es2pandaContextState): PluginExecutor[] | undefined; getPluginContext(): PluginContext; + getPluginHistory(): PluginStateId[]; + clear(): void; } function toCamelCase(str: string): string { @@ -71,25 +74,45 @@ function selectPlugins(plugins: Plugins[], stage: PluginState): PluginExecutor[] class MockPluginDriver implements PluginDriver { private sortedPlugins: Map; - private context: PluginContext; + private history: Set; + private context: PluginContext | undefined; + + private static instance: PluginDriver | undefined; constructor() { this.sortedPlugins = new Map(); + this.history = new Set(); this.context = new PluginContext(); } + public static getInstance(): PluginDriver { + if (!this.instance) { + this.instance = new MockPluginDriver(); + } + return this.instance; + } + + private collectHistory(state: PluginState, plugins: PluginExecutor[]) { + for (const plugin of plugins) { + this.history.add(`${state}:${plugin.name}`); + } + this.history.add(state); + } + public initPlugins(plugins: Plugins[]): void { const pluginsByState = new Map(); Object.values(arkts.Es2pandaContextState) .filter(isNumber) .forEach((it) => { - const selected = selectPlugins(plugins, stateName(it)); + const state = stateName(it); + const selected = selectPlugins(plugins, state); if (selected.length > 0) { pluginsByState.set(it, selected); } else { pluginsByState.set(it, undefined); } + this.collectHistory(state, selected); }); this.sortedPlugins = pluginsByState; @@ -100,8 +123,20 @@ class MockPluginDriver implements PluginDriver { } public getPluginContext(): PluginContext { + if (!this.context) { + this.context = new PluginContext(); + } return this.context; } + + public getPluginHistory(): PluginStateId[] { + return Array.from(this.history); + } + + public clear(): void { + this.sortedPlugins.clear(); + this.context = undefined; + } } export { stateName, MockPluginDriver }; diff --git a/arkui-plugins/test/utils/plugin-tester.ts b/arkui-plugins/test/utils/plugin-tester.ts index 710456e21ed4557b0241b4f7da0491bc3a62c99a..1f1a6a59d2a256b82b3924c8a7b92cf00a4cd441 100644 --- a/arkui-plugins/test/utils/plugin-tester.ts +++ b/arkui-plugins/test/utils/plugin-tester.ts @@ -13,29 +13,30 @@ * limitations under the License. */ -import { ArktsConfigBuilder, BuildConfig, CompileFileInfo, MockArktsConfigBuilder, ModuleInfo } from './artkts-config'; -import { MockPluginDriver, PluginDriver, stateName } from './plugin-driver'; -import { isNumber } from './safe-types'; +import { ArktsConfigBuilder, MockArktsConfigBuilder } from './artkts-config'; +import { MockPluginDriver } from './plugin-driver'; import { - canProceedToState, - destroyConfig, - destroyContext, - initGlobal, - resetConfig, - resetContext, -} from './global'; -import { insertPlugin } from './compile'; -import { PluginExecutor, Plugins, PluginState } from '../../common/plugin-context'; -import { TesterCache } from './cache'; -import * as arkts from '@koalaui/libarkts'; -import * as fs from 'fs'; + BuildConfig, + CompileFileInfo, + PluginStateId, + PluginTestContext, + Processor, + SingleProgramContext, + TraceOptions, +} from './shared-types'; +import { HashGenerator } from './hash-generator'; +import { PluginTestContextCache } from './cache'; +import { Plugins, PluginState, ProjectConfig } from '../../common/plugin-context'; +import { concatObject } from './serializable'; +import { ProcessorBuilder } from './processor-builder'; +import { MainProcessor } from './processors/main-processor'; type TestParams = Parameters; type SkipFirstParam = T extends [unknown, ...infer Rest] ? Rest : never; type PluginTestHooks = { - [K in PluginState | `${PluginState}:${string}`]?: SkipFirstParam; + [K in PluginStateId]?: SkipFirstParam; }; type TestHooks = { @@ -44,134 +45,143 @@ type TestHooks = { afterEach?: Parameters; }; -export interface PluginTestContext { - scriptSnapshot?: string; - errors?: string[]; - warnings?: string[]; -} - export interface PluginTesterOptions { stopAfter: PluginState; buildConfig?: BuildConfig; + projectConfig?: ProjectConfig; + tracing?: TraceOptions; } class PluginTester { - private configBuilder: ArktsConfigBuilder; - private pluginDriver: PluginDriver; + private hashId: string; private describe: string; - private cache: TesterCache; + private configBuilder: ArktsConfigBuilder; + private taskProcessor?: Processor; + private resolve?: Promise; - constructor(describe: string, buildConfig?: BuildConfig) { + constructor(describe: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig) { this.describe = describe; - this.configBuilder = new MockArktsConfigBuilder(buildConfig); - this.pluginDriver = new MockPluginDriver(); - this.cache = TesterCache.getInstance(); + this.hashId = HashGenerator.getInstance().dynamicSha1Id(describe, 13); + this.configBuilder = new MockArktsConfigBuilder(this.hashId, buildConfig, projectConfig); } - private loadPluginDriver(plugins: Plugins[]): void { - this.pluginDriver.initPlugins(plugins); + private clear(): void { + this.clearCache(); + this.configBuilder.clear(); + this.taskProcessor?.clear(); + MockPluginDriver.getInstance().clear(); } - private test( - key: PluginState | `${PluginState}:${string}`, - index: arkts.Es2pandaContextState, - testName: string, - pluginHooks: PluginTestHooks, - plugin?: PluginExecutor - ): void { - let cached: boolean = false; - const cacheKey: string = `${testName}-${key}`; - if (index > arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED) { - return; - } - if (canProceedToState(index)) { - arkts.proceedToState(index); - } - if (plugin) { - insertPlugin(this.pluginDriver, plugin, index); - this.captureContext(cacheKey); - cached = true; - } - const hook: SkipFirstParam | undefined = pluginHooks[key]; - if (!!hook) { - if (!cached) this.captureContext(cacheKey); - test(testName, hook[0]?.bind(this.cache.get(cacheKey)), hook[1]); + private clearCache(): void { + const moduleInfo = this.configBuilder.moduleInfos.get(this.configBuilder.moduleRootPath)!; + const fileHistory = moduleInfo.compileFileInfos.map((fileInfo) => fileInfo.fileName) ?? []; + const pluginHistory = MockPluginDriver.getInstance().getPluginHistory(); + for (const pluginStateId of pluginHistory) { + const abcKey = this.getCacheKey(pluginStateId); + const externalKey = this.getCacheKey(pluginStateId, true); + for (const fileName of fileHistory) { + PluginTestContextCache.getInstance().delete(`${abcKey}:${fileName}`); + PluginTestContextCache.getInstance().delete(`${externalKey}:${fileName}`); + } } } - private captureContext(cacheKey: string): void { - try { - // TODO: add error/warning handling after plugin - const context: PluginTestContext = this.cache.get(cacheKey) ?? {}; - const script: arkts.EtsScript = arkts.EtsScript.fromContext(); - context.scriptSnapshot = script.dumpSrc(); - this.cache.set(cacheKey, context); - } catch (e) { - // Do nothing + private getCacheKey(pluginStateId: PluginStateId, isExternal?: boolean) { + return [this.hashId, !!isExternal ? 'external' : 'abc', pluginStateId].join(':'); + } + + private prepareContext(pluginStateId: PluginStateId, fileInfos: CompileFileInfo[]): PluginTestContext { + const fileNames: string[] = fileInfos.map((fileInfo) => fileInfo.fileName); + + const abcKey = this.getCacheKey(pluginStateId); + const externalKey = this.getCacheKey(pluginStateId, true); + + const sourceContexts: Record = {}; + let declContexts: Record = {}; + fileNames.forEach((fileName) => { + const sourceKey = `${abcKey}:${fileName}`; + const sourceContext = PluginTestContextCache.getInstance().get(sourceKey) ?? {}; + if (!!sourceContext.declContexts) { + declContexts = concatObject(declContexts, sourceContext.declContexts); + delete sourceContext.declContexts; + } + sourceContexts[fileName] = sourceContext; + + const declKey = `${externalKey}:${fileName}`; + const declContext = PluginTestContextCache.getInstance().get(declKey) ?? {}; + declContexts = concatObject(declContexts, declContext.declContexts ?? {}); + }); + + return { sourceContexts, declContexts }; + } + + private findContext(testContext: PluginTestContext | undefined, fileName?: string): PluginTestContext | undefined { + if (!testContext) { + return undefined; } + if (!testContext.sourceContexts) { + return { declContexts: testContext.declContexts }; + } + const sourceContext = fileName + ? testContext.sourceContexts[fileName] + : Object.values(testContext.sourceContexts)[Symbol.iterator]().next().value; + return { ...sourceContext, declContexts: testContext.declContexts }; } - private proceedToState( - state: PluginState, - index: arkts.Es2pandaContextState, + private test( + pluginStateId: PluginStateId, testName: string, - pluginHooks: PluginTestHooks, - plugins?: PluginExecutor[] + hook: SkipFirstParam | undefined, + compileFiles: CompileFileInfo[], + fileName?: string ): void { - if (plugins && plugins.length > 0) { - plugins.forEach((plugin) => { - const pluginName: string = plugin.name; - const key: `${PluginState}:${string}` = `${state}:${pluginName}`; - this.test(key, index, `[${key}] ${testName}`, pluginHooks, plugin); - }); + if (!!hook) { + const that = this; + test( + testName, + async () => { + let context: PluginTestContext | undefined; + await that.resolve?.then(async () => { + const testContext = this.prepareContext(pluginStateId, compileFiles ?? []); + context = this.findContext(testContext, fileName); + }); + hook[0]?.bind(context)(undefined as any); + }, + hook[1] + ); } - this.test(state, index, `[${state}] ${testName}`, pluginHooks); } - private singleFileCompile( - fileInfo: CompileFileInfo, - moduleInfo: ModuleInfo, - testName: string, - pluginHooks: PluginTestHooks, - stopAfter: PluginState - ): void { - let shouldStop: boolean = false; - - Object.values(arkts.Es2pandaContextState) - .filter(isNumber) - .forEach((it) => { - if (shouldStop) { - return; - } - const state: PluginState = stateName(it); - const plugins: PluginExecutor[] | undefined = this.pluginDriver.getSortedPlugins(it); - this.proceedToState( - state, - it, - `${moduleInfo.packageName} - ${fileInfo.fileName}: ${testName}`, - pluginHooks, - plugins - ); - shouldStop = state === stopAfter; - }); + private pluginTests(key: PluginStateId, testName: string, pluginHooks: PluginTestHooks): void { + const moduleInfo = this.configBuilder.moduleInfos.get(this.configBuilder.moduleRootPath)!; + const compileFiles = moduleInfo.compileFileInfos; + compileFiles?.forEach((fileInfo) => { + const fileName = fileInfo.fileName; + const name: string = `[${key}] ${moduleInfo.packageName} - ${fileName}: ${testName}`; + this.test(key, name, pluginHooks[`${key}:${fileName}`], compileFiles, fileName); + }); + const name: string = `[${key}] ${moduleInfo.packageName}: ${testName}`; + this.test(key, name, pluginHooks[key], compileFiles); } - private traverseFile(testName: string, pluginHooks: PluginTestHooks, stopAfter: PluginState): void { - let once: boolean = false; - this.configBuilder.moduleInfos.forEach((moduleInfo) => { - moduleInfo.compileFileInfos.forEach((fileInfo) => { - if (!once) { - initGlobal(fileInfo, this.configBuilder.isDebug); - once = true; - } else { - const source: string = fs.readFileSync(fileInfo.filePath).toString(); - resetContext(source); - } - this.singleFileCompile(fileInfo, moduleInfo, testName, pluginHooks, stopAfter); - }); + private compileTests(testName: string, pluginHooks: PluginTestHooks): void { + const history = MockPluginDriver.getInstance().getPluginHistory(); + history.forEach((key) => { + this.pluginTests(key, testName, pluginHooks); }); } + private async compile(plugins: Plugins[], stopAfter?: PluginState, tracing?: TraceOptions): Promise { + this.taskProcessor = ProcessorBuilder.build( + MainProcessor, + this.hashId, + this.configBuilder.buildConfig, + this.configBuilder.projectConfig, + tracing + ); + return this.taskProcessor.invokeWorkers(plugins, stopAfter); + } + run( testName: string, plugins: Plugins[], @@ -180,11 +190,11 @@ class PluginTester { testHooks?: TestHooks ): void { if (!!options.buildConfig) { - this.configBuilder = new MockArktsConfigBuilder(options.buildConfig); + this.configBuilder = this.configBuilder.withBuildConfig(options.buildConfig); + } + if (!!options.projectConfig) { + this.configBuilder = this.configBuilder.withProjectConfig(options.projectConfig); } - - this.cache.clear(); - this.loadPluginDriver(plugins); const that = this; describe(this.describe, () => { @@ -197,11 +207,13 @@ class PluginTester { if (testHooks?.afterEach) { afterEach(...testHooks.afterEach); } + afterAll(() => { - destroyContext(); - destroyConfig(); + that.clear(); }); - that.traverseFile(testName, pluginHooks, options.stopAfter); + + that.resolve = that.compile(plugins, options.stopAfter, options.tracing); + that.compileTests(testName, pluginHooks); }); } } diff --git a/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts new file mode 100644 index 0000000000000000000000000000000000000000..f6a2c0c13dd998eaa4fdb841a11a92f14291124e --- /dev/null +++ b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 { PluginContext, Plugins } from '../../../common/plugin-context'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { MemoVisitor } from '../../../collectors/memo-collectors/memo-visitor'; + +/** + * AfterCheck before-memo-visit and cache any node that should be unmemoized with no recheck AST. + */ +export const beforeMemoNoRecheck: Plugins = { + name: 'before-memo-no-recheck', + checked(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.astNode; + const builderLambdaTransformer = new MemoVisitor(); + const programVisitor = new ProgramVisitor({ + pluginName: beforeMemoNoRecheck.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [builderLambdaTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; + }, +}; \ No newline at end of file diff --git a/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts b/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts index 69e2b23adfc79c9fc7b138e1f011b07b6b092efb..a770c96b00574186901146a05d6ba5eed9b019f7 100644 --- a/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/builder-lambda-no-recheck.ts @@ -26,11 +26,11 @@ export const builderLambdaNoRecheck: Plugins = { name: 'builder-lambda-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; - const builderLambdaTransformer = new BuilderLambdaTransformer(); + const builderLambdaTransformer = new BuilderLambdaTransformer(this.getProjectConfig()); const programVisitor = new ProgramVisitor({ pluginName: builderLambdaNoRecheck.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, @@ -40,7 +40,6 @@ export const builderLambdaNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/plugins/index.ts b/arkui-plugins/test/utils/plugins/index.ts index 4858b84b2f96416bdcffd8779f14baf792ffef95..ab232c217a4c678d522d4e2c7490ba8723e8c5fd 100644 --- a/arkui-plugins/test/utils/plugins/index.ts +++ b/arkui-plugins/test/utils/plugins/index.ts @@ -17,6 +17,7 @@ export * from './struct-to-component'; // AfterCheck +export * from './before-memo-no-recheck'; export * from './builder-lambda-no-recheck'; export * from './memo-no-recheck'; export * from './struct-no-recheck'; diff --git a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts index ff018cec51611f2fcc51485a17e185fca9ef357e..9396b34e5778d0fe20653018da0e6d8076ccb594 100644 --- a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts @@ -16,12 +16,13 @@ import * as arkts from '@koalaui/libarkts'; import { PluginContext, Plugins } from '../../../common/plugin-context'; import { ProgramVisitor } from '../../../common/program-visitor'; -import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { EXTERNAL_SOURCE_PREFIX_NAMES, EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK } from '../../../common/predefines'; import { PositionalIdTracker } from '../../../memo-plugins/utils'; import { ParameterTransformer } from '../../../memo-plugins/parameter-transformer'; import { ReturnTransformer } from '../../../memo-plugins/return-transformer'; import { SignatureTransformer } from '../../../memo-plugins/signature-transformer'; import { FunctionTransformer } from '../../../memo-plugins/function-transformer'; +import { InternalsTransformer } from '../../../memo-plugins/internal-transformer'; /** * AfterCheck unmemoizeTransform with no recheck AST. @@ -29,8 +30,9 @@ import { FunctionTransformer } from '../../../memo-plugins/function-transformer' export const memoNoRecheck: Plugins = { name: 'memo-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { + const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode; let program = arkts.getOrUpdateGlobalContext(contextPtr).program; let script = program.astNode; const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); @@ -39,20 +41,30 @@ export const memoNoRecheck: Plugins = { }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, + internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected() }); + const skipPrefixNames = isFrameworkMode + ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK + : EXTERNAL_SOURCE_PREFIX_NAMES; const programVisitor = new ProgramVisitor({ pluginName: memoNoRecheck.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, visitors: [functionTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + skipPrefixNames, pluginContext: this, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); script = program.astNode; return script; } diff --git a/arkui-plugins/test/utils/plugins/recheck.ts b/arkui-plugins/test/utils/plugins/recheck.ts index 085d3731e44fbfcb24b73b4b1d32843ce40bc849..2c3559df4107be301827c816f81442bba90b9abf 100644 --- a/arkui-plugins/test/utils/plugins/recheck.ts +++ b/arkui-plugins/test/utils/plugins/recheck.ts @@ -23,7 +23,7 @@ export const recheck: Plugins = { name: 'recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; diff --git a/arkui-plugins/test/utils/plugins/struct-no-recheck.ts b/arkui-plugins/test/utils/plugins/struct-no-recheck.ts index c564b71c780c590b555aec03d0b7b10db57fe7f6..87a7bdbd0cdec0fc276dacafed76f02403536644 100644 --- a/arkui-plugins/test/utils/plugins/struct-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/struct-no-recheck.ts @@ -26,7 +26,7 @@ export const structNoRecheck: Plugins = { name: 'struct-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; @@ -40,7 +40,6 @@ export const structNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/plugins/struct-to-component.ts b/arkui-plugins/test/utils/plugins/struct-to-component.ts index 14d494c050b2e384430a100c31fa94684ffef1e7..ddb0281d2428a01a5939dcf29281100ab5650a04 100644 --- a/arkui-plugins/test/utils/plugins/struct-to-component.ts +++ b/arkui-plugins/test/utils/plugins/struct-to-component.ts @@ -26,11 +26,13 @@ export const structToComponent: Plugins = { name: 'struct-to-component', parsed(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; - const componentTransformer = new ComponentTransformer(); + const componentTransformer = new ComponentTransformer({ + projectConfig: this.getProjectConfig(), + }); const programVisitor = new ProgramVisitor({ pluginName: structToComponent.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, diff --git a/arkui-plugins/test/utils/plugins/ui-no-recheck.ts b/arkui-plugins/test/utils/plugins/ui-no-recheck.ts index dec4b5296d0c13dceb7a2d3749e09306f2adb4a0..5fbc6c3fb4d7f04c8f2452611b3fb42abb0e07c5 100644 --- a/arkui-plugins/test/utils/plugins/ui-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/ui-no-recheck.ts @@ -26,7 +26,7 @@ export const uiNoRecheck: Plugins = { name: 'ui-no-recheck', checked(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; @@ -40,7 +40,6 @@ export const uiNoRecheck: Plugins = { }); program = programVisitor.programVisitor(program); script = program.astNode; - arkts.GlobalInfo.getInfoInstance().reset(); return script; } return script; diff --git a/arkui-plugins/test/utils/processor-builder.ts b/arkui-plugins/test/utils/processor-builder.ts new file mode 100644 index 0000000000000000000000000000000000000000..7be32e773b639ff283e5484fe125d6c5d164575b --- /dev/null +++ b/arkui-plugins/test/utils/processor-builder.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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 { ProjectConfig } from 'common/plugin-context'; +import { BuildConfig, Processor, TraceOptions } from './shared-types'; + +class ProcessorBuilder { + static build( + Processor: { + new ( + hashId: string, + buildConfig?: BuildConfig, + projectConfig?: ProjectConfig, + tracing?: TraceOptions + ): Processor; + }, + hashId: string, + buildConfig?: BuildConfig, + projectConfig?: ProjectConfig, + tracing?: TraceOptions + ): Processor { + return new Processor(hashId, buildConfig, projectConfig, tracing); + } +} + +export { ProcessorBuilder }; diff --git a/arkui-plugins/test/utils/processors/base-processor.ts b/arkui-plugins/test/utils/processors/base-processor.ts new file mode 100644 index 0000000000000000000000000000000000000000..53e79ea52df1b646c673d71942e669edc79c9fa2 --- /dev/null +++ b/arkui-plugins/test/utils/processors/base-processor.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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 { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { mockBuildConfig, mockProjectConfig } from '../artkts-config'; +import { ArkTSConfigContextCache } from '../cache'; +import { BuildConfig, CompileFileInfo, Processor, TraceOptions } from '../shared-types'; + +abstract class BaseProcessor implements Processor { + hashId: string; + buildConfig: BuildConfig; + projectConfig: ProjectConfig; + tracing: TraceOptions; + cacheDir: string; + arktsConfigFile: string; + compileFiles: Map; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + this.hashId = hashId; + this.tracing = tracing ?? { externalSourceNames: [] }; + + const _buildConfig: BuildConfig = buildConfig ?? mockBuildConfig(); + this.buildConfig = _buildConfig; + this.cacheDir = _buildConfig.cachePath; + this.arktsConfigFile = this.getArktsConfigFile(); + this.compileFiles = this.getCompileFiles(); + + const _projectConfig: ProjectConfig = projectConfig ?? mockProjectConfig(); + this.projectConfig = _projectConfig; + } + + private getArktsConfigFile(): string { + const arktsConfigFile = ArkTSConfigContextCache.getInstance().get(this.hashId)?.arktsConfigFile; + if (!arktsConfigFile) { + const err = `[${this.hashId}] TaskProcessor cannot get arktsConfigFile`; + console.error(err); + throw new Error(err); + } + return arktsConfigFile; + } + + private getCompileFiles(): Map { + const compileFiles = ArkTSConfigContextCache.getInstance().get(this.hashId)?.compileFiles; + if (!compileFiles) { + const err = `[${this.hashId}] TaskProcessor cannot get compileFiles`; + console.error(err); + throw new Error(err); + } + return compileFiles; + } + + abstract invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise; + + abstract clear(): void; +} + +export { BaseProcessor }; diff --git a/arkui-plugins/test/utils/processors/main-processor.ts b/arkui-plugins/test/utils/processors/main-processor.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b8f2399d3c3d80d42c104c93ac7447592efaaf8 --- /dev/null +++ b/arkui-plugins/test/utils/processors/main-processor.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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 EventEmitter from 'events'; +import { + BuildConfig, + CompileFileInfo, + CompileStrategy, + JobInfo, + PluginTestContext, + ProcessEvent, + TraceOptions, +} from '../shared-types'; +import { BaseProcessor } from './base-processor'; +import { PluginTestContextCache } from '../cache'; +import { concatObject, serializable } from '../serializable'; +import { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { createGlobalConfig, destroyGlobalConfig } from '../global'; +import { compileAbcWithExternal } from '../compile'; + +class MainProcessor extends BaseProcessor { + filePaths: string[]; + + readonly emitter: EventEmitter = new EventEmitter(); + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + super(hashId, buildConfig, projectConfig, tracing); + this.filePaths = this.getCompileFilePaths(); + } + + private getCompileFilePaths(): string[] { + return Array.from(this.compileFiles.values()).map((fileInfo) => fileInfo.filePath); + } + + private subscribe(): void { + this.emitter.on('TASK_COLLECT', (msg) => { + const sourceType = 'abc'; + const pluginStateId = msg.pluginStateId; + const fileName = msg.fileName; + const pluginTestContext = msg.pluginTestContext as PluginTestContext; + const key = `${this.hashId}:${sourceType}:${pluginStateId}:${fileName}`; + let currentPluginTestContext; + if (PluginTestContextCache.getInstance().has(key)) { + const oldContext = PluginTestContextCache.getInstance().get(key)!; + currentPluginTestContext = concatObject(oldContext, pluginTestContext); + } else { + currentPluginTestContext = pluginTestContext; + } + PluginTestContextCache.getInstance().set(key, currentPluginTestContext); + }); + } + + private assignTask(fileInfo: CompileFileInfo, plugins: Plugins[], stopAfter?: PluginState): void { + const jobInfo: JobInfo = { + id: 'compile-abc-with-external', + isCompileAbc: CompileStrategy.ABC_WTIH_EXTERNAL, + compileFileInfo: fileInfo, + buildConfig: serializable(this.buildConfig), + projectConfig: serializable(this.projectConfig), + plugins, + stopAfter, + filePaths: this.filePaths, + }; + compileAbcWithExternal(this.emitter, jobInfo, this.tracing); + } + + async invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise { + return new Promise((resolve) => { + const fileInfo: CompileFileInfo = this.compileFiles.values().next().value!; + const config = createGlobalConfig(fileInfo, true, false); + this.subscribe(); + this.emitter.on('TASK_FINISH', (msg) => { + console.log('All tasks completed. Exiting...'); + destroyGlobalConfig(config, false); + resolve(); + }); + this.assignTask(fileInfo, plugins, stopAfter); + }); + } + + clear(): void {} +} + +export { MainProcessor }; diff --git a/arkui-plugins/test/utils/processors/task-processor.ts b/arkui-plugins/test/utils/processors/task-processor.ts new file mode 100644 index 0000000000000000000000000000000000000000..a66b89eb4b8008351d27efd2b6e547c5d646035c --- /dev/null +++ b/arkui-plugins/test/utils/processors/task-processor.ts @@ -0,0 +1,517 @@ +/* + * Copyright (c) 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 os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as child_process from 'child_process'; +import EventEmitter from 'events'; +import { + CompileStrategy, + type BuildConfig, + type CompileFileInfo, + type JobInfo, + type PluginTestContext, + type ProcessEvent, + type TraceOptions, +} from '../shared-types'; +import { + DECL_ETS_SUFFIX, + ensurePathExists, + getFileName, + MOCK_DEP_INPUT_FILE_NAME, + MOCK_FILE_DEP_FILE_NAME, +} from '../path-config'; +import { FileDependencyContextCache, PluginTestContextCache } from '../cache'; +import { HashGenerator } from '../hash-generator'; +import { createGlobalConfig, createGlobalContextPtr, destroyGlobalConfig, destroyGlobalContextPtr } from '../global'; +import { Plugins, PluginState, ProjectConfig } from '../../../common/plugin-context'; +import { concatObject, serializable } from '../serializable'; +import { compileAbc, compileExternalProgram } from '../compile'; +import { BaseProcessor } from './base-processor'; + +interface Job { + id: string; + isDeclFile: boolean; + isInCycle?: boolean; + fileList: string[]; + dependencies: string[]; + dependants: string[]; + isAbcJob: boolean; +} + +interface Queues { + externalProgramQueue: Job[]; + abcQueue: Job[]; +} + +interface FileDepsInfo { + dependencies: Record; + dependants: Record; +} + +interface WorkerInfo { + isIdle: boolean; +} + +function getDepAnalyzerCmd( + depAnalyzerPath: string, + depInputFile: string, + entryFiles: Set, + outputFile: string, + arktsConfigFile: string +): string[] { + const depAnalyzerCmd: string[] = [`"${depAnalyzerPath}"`]; + + let depInputContent = ''; + entryFiles.forEach((file: string) => { + depInputContent += file + os.EOL; + }); + fs.writeFileSync(depInputFile, depInputContent); + + depAnalyzerCmd.push(`@"${depInputFile}"`); + depAnalyzerCmd.push(`--output=${outputFile}`); + depAnalyzerCmd.push(`--arktsconfig=${arktsConfigFile}`); + + return depAnalyzerCmd; +} + +function dfs(node: string, visited: Set, adjacencyList: Record, order: string[]) { + visited.add(node); + for (const neighbor of adjacencyList[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor, visited, adjacencyList, order); + } + } + order.push(node); +} + +function reverseDfs( + node: string, + component: Set, + visited: Set, + reverseAdjacencyList: Record +) { + visited.add(node); + component.add(node); + for (const neighbor of reverseAdjacencyList[node]) { + if (!visited.has(neighbor)) { + reverseDfs(neighbor, component, visited, reverseAdjacencyList); + } + } +} + +function findStronglyConnectedComponents(graph: FileDepsInfo): Map> { + const adjacencyList: Record = {}; + const reverseAdjacencyList: Record = {}; + const allNodes = new Set(); + for (const node in graph.dependencies) { + allNodes.add(node); + graph.dependencies[node].forEach((dep) => allNodes.add(dep)); + } + for (const node in graph.dependants) { + allNodes.add(node); + graph.dependants[node].forEach((dep) => allNodes.add(dep)); + } + Array.from(allNodes).forEach((node) => { + adjacencyList[node] = graph.dependencies[node] || []; + reverseAdjacencyList[node] = graph.dependants[node] || []; + }); + const visited = new Set(); + const order: string[] = []; + Array.from(allNodes).forEach((node) => { + if (!visited.has(node)) { + dfs(node, visited, adjacencyList, order); + } + }); + visited.clear(); + const components = new Map>(); + for (let i = order.length - 1; i >= 0; i--) { + const node = order[i]; + if (!visited.has(node)) { + const component = new Set(); + reverseDfs(node, component, visited, reverseAdjacencyList); + if (component.size > 1) { + const sortedFiles = Array.from(component).sort(); + const hashKey = HashGenerator.getInstance().staticSha1Id(sortedFiles.join('|'), 13); + components.set(hashKey, component); + } + } + } + return components; +} + +function getJobDependencies(fileDeps: string[], cycleFiles: Map): Set { + const depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!cycleFiles.has(file)) { + depJobList.add('0' + file); + } else { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } + }); + + return depJobList; +} + +function getJobDependants(fileDeps: string[], cycleFiles: Map): Set { + let depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!file.endsWith(DECL_ETS_SUFFIX)) { + depJobList.add('1' + file); + } + if (cycleFiles.has(file)) { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } else { + depJobList.add('0' + file); + } + }); + + return depJobList; +} + +function insertJobDependantsButSelf(jobMap: Record, id: string, dependants: Set): Set { + jobMap[id].dependants.forEach((dep) => { + dependants.add(dep); + }); + if (dependants.has(id)) { + dependants.delete(id); + } + return dependants; +} + +function createExternalProgramJob(id: string, fileList: string[], dependencies: Set, isInCycle?: boolean): Job { + return { + id, + fileList, + isDeclFile: true, + isInCycle, + isAbcJob: false, + dependencies: Array.from(dependencies), + dependants: [], + }; +} + +function createAbcJob(id: string, fileList: string[], dependencies: Set, isInCycle?: boolean): Job { + return { + id, + isDeclFile: false, + isInCycle, + isAbcJob: true, + fileList, + dependencies: Array.from(dependencies), + dependants: [], + }; +} + +function addJobToQueues(job: Job, queues: Queues): void { + if (queues.externalProgramQueue.some((j) => j.id === job.id) || queues.abcQueue.some((j) => j.id === job.id)) { + return; + } + if (!job.isAbcJob) { + queues.externalProgramQueue.push(job); + } else { + queues.abcQueue.push(job); + } +} + +class TaskProcessor extends BaseProcessor { + entryFiles: Set; + depAnalyzerPath: string; + depInputFile: string; + fileDepsInfoJson: string; + jobMap: Record; + jobQueues: Queues; + + readonly emitter: EventEmitter = new EventEmitter(); + private worker!: WorkerInfo; + + constructor(hashId: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig, tracing?: TraceOptions) { + super(hashId, buildConfig, projectConfig, tracing); + this.entryFiles = new Set(this.buildConfig.compileFiles as string[]); + this.depAnalyzerPath = this.buildConfig.depAnalyzerPath; + this.depInputFile = path.resolve(this.buildConfig.cachePath, this.hashId, MOCK_DEP_INPUT_FILE_NAME); + this.fileDepsInfoJson = path.resolve(this.buildConfig.cachePath, this.hashId, MOCK_FILE_DEP_FILE_NAME); + + this.generateFileDependencies(); + this.cacheFileDependencies(); + this.jobMap = this.collectCompileJobs(); + this.jobQueues = this.initCompileQueues(); + } + + private cacheFileDependencies(): void { + const depInputFile: string = this.arktsConfigFile; + const fileDepsInfoJson: string = this.fileDepsInfoJson; + FileDependencyContextCache.getInstance().set(this.hashId, { depInputFile, fileDepsInfoJson }); + } + + private generateFileDependencies(): void { + ensurePathExists(this.depInputFile); + ensurePathExists(this.fileDepsInfoJson); + const depAnalyzerCmd: string[] = getDepAnalyzerCmd( + this.depAnalyzerPath, + this.depInputFile, + this.entryFiles, + this.fileDepsInfoJson, + this.arktsConfigFile + ); + const depAnalyzerCmdStr: string = depAnalyzerCmd.join(' '); + try { + child_process.execSync(depAnalyzerCmdStr).toString(); + } catch (error) { + const err = `[${this.hashId}] TaskProcessor generateFileDependencies failed: ${error}`; + console.error(err); + throw new Error(err); + } + } + + private collectJobsInDependants( + jobMap: Record, + cycleFiles: Map, + dependantMap: [string, string[]] + ): void { + const [key, value] = dependantMap; + const dependants = getJobDependants(value, cycleFiles); + if (cycleFiles.has(key)) { + const externalProgramJobIds = cycleFiles.get(key)!; + externalProgramJobIds.forEach((id) => { + jobMap[id].dependants = Array.from(insertJobDependantsButSelf(jobMap, id, dependants)); + }); + } else { + const id = '0' + key; + jobMap[id].dependants = Array.from(insertJobDependantsButSelf(jobMap, id, dependants)); + } + } + + private collectJobsInDependencies( + jobMap: Record, + cycleFiles: Map, + cycleGroups: Map>, + dependencyMap: [string, string[]] + ): void { + const [key, value] = dependencyMap; + const dependencies = getJobDependencies(value, cycleFiles); + // Create generate abc job + if (!key.endsWith(DECL_ETS_SUFFIX)) { + const abcJobId: string = '1' + key; + jobMap[abcJobId] = createAbcJob(abcJobId, [key], dependencies, cycleFiles.has(key)); + } + // Create cache external job + if (cycleFiles.has(key)) { + const externalProgramJobIds = cycleFiles.get(key)!; + externalProgramJobIds.forEach((id) => { + const fileList: string[] = Array.from(cycleGroups.get(id)!); + if (dependencies.has(id)) { + dependencies.delete(id); + } + jobMap[id] = createExternalProgramJob(id, fileList, dependencies, true); + }); + } else { + const id = '0' + key; + const fileList: string[] = [key]; + if (dependencies.has(id)) { + dependencies.delete(id); + } + jobMap[id] = createExternalProgramJob(id, fileList, dependencies); + } + // register compileFiles for declaration files + if (key.endsWith(DECL_ETS_SUFFIX)) { + const fileInfo: CompileFileInfo = { + filePath: key, + dependentFiles: [], + abcFilePath: '', + arktsConfigFile: this.arktsConfigFile, + fileName: getFileName(key), + stdLibPath: '', + }; + + if (!this.compileFiles.has(key)) { + this.compileFiles.set(key, fileInfo); + } + } + } + + private collectCompileJobs(): Record { + const data = fs.readFileSync(this.fileDepsInfoJson, 'utf-8'); + if (data.length === 0) { + const err = `[${this.hashId}] TaskProcessor cannot read fileDepsInfoJson`; + console.error(err); + throw new Error(err); + } + const fileDepsInfo: FileDepsInfo = JSON.parse(data) as FileDepsInfo; + Object.keys(fileDepsInfo.dependants).forEach((file) => { + if (!(file in fileDepsInfo.dependencies)) { + fileDepsInfo.dependencies[file] = []; + } + }); + const cycleGroups = findStronglyConnectedComponents(fileDepsInfo); + const cycleFiles: Map = new Map(); + cycleGroups.forEach((value: Set, key: string) => { + value.forEach((file) => { + cycleFiles.set(file, [key]); + }); + }); + const jobMap: Record = {}; + Object.entries(fileDepsInfo.dependencies).forEach((dependencyMap) => { + this.collectJobsInDependencies(jobMap, cycleFiles, cycleGroups, dependencyMap); + }); + Object.entries(fileDepsInfo.dependants).forEach((dependantMap) => { + this.collectJobsInDependants(jobMap, cycleFiles, dependantMap); + }); + return jobMap; + } + + private initCompileQueues(): Queues { + const queues: Queues = { externalProgramQueue: [], abcQueue: [] }; + Object.values(this.jobMap).forEach((job) => { + if (job.dependencies.length === 0) { + addJobToQueues(job, queues); + } + }); + return queues; + } + + private assignTaskToIdleWorker( + processingJobs: Set, + globalContextPtr: number, + plugins: Plugins[], + stopAfter?: PluginState + ) { + let job: Job | undefined; + let jobInfo: JobInfo | undefined; + if (this.jobQueues.externalProgramQueue.length > 0) { + job = this.jobQueues.externalProgramQueue.shift()!; + jobInfo = { + id: job.id, + isCompileAbc: CompileStrategy.EXTERNAL, + }; + } else if (this.jobQueues.abcQueue.length > 0) { + job = this.jobQueues.abcQueue.shift()!; + jobInfo = { + id: job.id, + isCompileAbc: CompileStrategy.ABC, + }; + } + + if (!!job && !!jobInfo) { + processingJobs.add(job.id); + jobInfo.compileFileInfo = this.compileFiles.get(job.fileList[0]); + jobInfo.buildConfig = serializable(this.buildConfig); + jobInfo.projectConfig = serializable(this.projectConfig); + jobInfo.plugins = plugins; + jobInfo.globalContextPtr = globalContextPtr; + jobInfo.stopAfter = stopAfter; + + this.worker.isIdle = false; + this.emitter.emit('ASSIGN_TASK', { jobInfo }); + } + } + + private checkAllTasksDone(): boolean { + return this.jobQueues.externalProgramQueue.length === 0 && this.worker.isIdle; + } + + private addDependantJobToQueues(jobId: string): void { + const completedJob = this.jobMap[jobId]; + completedJob.dependants.forEach((depJobId) => { + const depJob = this.jobMap[depJobId]; + const depIndex = depJob.dependencies.indexOf(jobId); + if (depIndex !== -1) { + depJob.dependencies.splice(depIndex, 1); + if (depJob.dependencies.length === 0) { + addJobToQueues(depJob, this.jobQueues); + } + } + }); + } + + private subscribe() { + this.emitter.on('ASSIGN_TASK', (msg) => { + const job = msg.jobInfo; + if (job.isCompileAbc === CompileStrategy.ABC) { + compileAbc(this.emitter, job, this.tracing); + } else if (job.isCompileAbc === CompileStrategy.EXTERNAL) { + compileExternalProgram(this.emitter, job, this.tracing); + } + this.emitter.emit('TASK_FINISH', { jobId: job.id }); + }); + this.emitter.on('EXIT', () => { + this.worker.isIdle = true; + console.log(`worker exiting...`); + }); + this.emitter.on('TASK_COLLECT', (msg) => { + const jobId = msg.jobId; + const job = this.jobMap[jobId]; + const sourceType = job.isAbcJob ? 'abc' : 'external'; + const pluginStateId = msg.pluginStateId; + const fileName = msg.fileName; + const pluginTestContext = msg.pluginTestContext as PluginTestContext; + const key = `${this.hashId}:${sourceType}:${pluginStateId}:${fileName}`; + let currentPluginTestContext; + if (PluginTestContextCache.getInstance().has(key)) { + const oldContext = PluginTestContextCache.getInstance().get(key)!; + currentPluginTestContext = concatObject(oldContext, pluginTestContext); + } else { + currentPluginTestContext = pluginTestContext; + } + PluginTestContextCache.getInstance().set(key, currentPluginTestContext); + }); + } + + async invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise { + const processingJobs = new Set(); + return new Promise((resolve) => { + const files: string[] = []; + Object.values(this.jobMap).forEach((job) => { + for (let i = 0; i < job.fileList.length; i++) { + files.push(job.fileList[i]); + } + }); + const fileInfo: CompileFileInfo = this.compileFiles.values().next().value!; + const config = createGlobalConfig(fileInfo); + const globalContextPtr = createGlobalContextPtr(config, files); + this.subscribe(); + this.emitter.on('TASK_FINISH', (msg) => { + this.worker.isIdle = true; + const jobId = msg.jobId; + processingJobs.delete(jobId); + this.addDependantJobToQueues(jobId); + const hasRemainingTask = + this.jobQueues.externalProgramQueue.length > 0 || this.jobQueues.abcQueue.length > 0; + if (hasRemainingTask) { + this.assignTaskToIdleWorker(processingJobs, globalContextPtr, plugins, stopAfter); + } else if (this.checkAllTasksDone()) { + console.log('All tasks completed. Exiting...'); + this.emitter.emit('EXIT'); + destroyGlobalContextPtr(globalContextPtr); + destroyGlobalConfig(config); + resolve(); + } + }); + this.worker = { isIdle: true }; + this.assignTaskToIdleWorker(processingJobs, globalContextPtr, plugins, stopAfter); + }); + } + + clear(): void { + FileDependencyContextCache.getInstance().delete(this.hashId); + } +} + +export { TaskProcessor }; diff --git a/arkui-plugins/test/utils/safe-types.ts b/arkui-plugins/test/utils/safe-types.ts index 728c06501cffc9330993fb6c4d91e3fec93c7bad..e622c5081e46d5022518bac7620ac19fc1718fe3 100644 --- a/arkui-plugins/test/utils/safe-types.ts +++ b/arkui-plugins/test/utils/safe-types.ts @@ -17,4 +17,12 @@ function isNumber(value: any): value is number { return typeof value === `number`; } -export { isNumber }; +function isString(value: any): value is string { + return typeof value === 'string'; +} + +function isBitInt(value: any): value is bigint { + return typeof value === 'bigint'; +} + +export { isNumber, isString, isBitInt }; diff --git a/arkui-plugins/test/utils/serializable.ts b/arkui-plugins/test/utils/serializable.ts new file mode 100644 index 0000000000000000000000000000000000000000..7747d78ab7ce1057bfe9b80c5b2593f2ac6cff57 --- /dev/null +++ b/arkui-plugins/test/utils/serializable.ts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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 { isBitInt } from './safe-types'; + +function serializable(object: T, ignoreKeys?: string[]): T { + const jsonStr = JSON.stringify(object, (key, value) => { + if (isBitInt(value)) { + return undefined; + } + if (ignoreKeys?.includes(key)) { + return undefined; + } + return value; + }); + return JSON.parse(jsonStr); +} + +type JSONObject = { [key: string]: any }; + +function concatObject(obj1: T | undefined, obj2: T | undefined): T { + const _obj1: JSONObject = obj1 ?? {}; + const _obj2: JSONObject = obj2 ?? {}; + const result: JSONObject = { ..._obj1 }; + + Object.keys(_obj2).forEach((key) => { + if (result.hasOwnProperty(key)) { + if ( + typeof _obj2[key] === 'object' && + typeof result[key] === 'object' && + _obj2[key] !== null && + result[key] !== null + ) { + result[key] = concatObject(result[key], _obj2[key]); + } else { + result[key] = _obj2[key]; + } + } else { + result[key] = _obj2[key]; + } + }); + + return result as T; +} + +export { serializable, concatObject }; diff --git a/arkui-plugins/test/utils/shared-types.ts b/arkui-plugins/test/utils/shared-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..83c365d53308bdf262bb294402a004f6de62d555 --- /dev/null +++ b/arkui-plugins/test/utils/shared-types.ts @@ -0,0 +1,143 @@ +/* + * Copyright (c) 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 type { Plugins, PluginState, ProjectConfig } from '../../common/plugin-context'; + +export type PluginTesterId = string | `${string}:${string}`; + +export type PluginStateId = PluginState | `${PluginState}:${PluginTesterId}`; + +export interface SingleProgramContext { + scriptSnapshot?: string; + errors?: string[]; + warnings?: string[]; +} + +export interface PluginTestContext extends SingleProgramContext { + declContexts?: Record; + sourceContexts?: Record; +} + +export interface ArkTSConfigContext { + arktsConfigFile: string; + compileFiles: Map; +} + +export interface FileDependencyContext { + depInputFile: string; + fileDepsInfoJson: string; +} + +export interface CompileFileInfo { + fileName: string; + filePath: string; + dependentFiles: string[]; + abcFilePath: string; + arktsConfigFile: string; + stdLibPath: string; +} + +export interface BuildConfig { + packageName: string; + compileFiles: string[]; + loaderOutPath: string; + cachePath: string; + pandaSdkPath: string; + apiPath: string; + kitsPath: string; + depAnalyzerPath: string; + sourceRoots: string[]; + moduleRootPath: string; + dependentModuleList: DependentModule[]; + projectConfig?: ProjectConfig; +} + +export type ModuleType = 'har' | 'feature' | 'entry' | string; // TODO: module type unclear + +export interface DependentModule { + packageName: string; + moduleName: string; + moduleType: ModuleType; + modulePath: string; + sourceRoots: string[]; + entryFile: string; +} + +export interface JobInfo { + id: string; + isCompileAbc: CompileStrategy; + compileFileInfo?: CompileFileInfo; + buildConfig?: BuildConfig; + projectConfig?: ProjectConfig; + plugins?: Plugins[]; + globalContextPtr?: number; + stopAfter?: PluginState; + filePaths?: string[]; +} + +export interface TraceOptions { + externalSourceNames: string[]; +} + +export interface ProcessTaskFinishEvent { + type: 'TASK_FINISH'; + jobId: string; +} + +export interface ProcessExitEvent { + type: 'EXIT'; +} + +export interface ProcessAssignTaskEvent { + type: 'ASSIGN_TASK'; + jobInfo: JobInfo; +} + +export interface ProcessTaskCollectEvent { + type: 'TASK_COLLECT'; + jobId: string; + pluginStateId: PluginStateId; + pluginTestContext: PluginTestContext; + fileName: string; +} + +export type ProcessEvents = + | ProcessAssignTaskEvent + | ProcessTaskFinishEvent + | ProcessTaskCollectEvent + | ProcessExitEvent; + +export type ProcessEvent = { + [E in ProcessEvents as E['type']]: Omit[]; +}; + +export interface Processor { + hashId: string; + buildConfig: BuildConfig; + projectConfig?: ProjectConfig; + tracing: TraceOptions; + cacheDir: string; + arktsConfigFile: string; + compileFiles: Map; + + invokeWorkers(plugins: Plugins[], stopAfter?: PluginState): Promise; + clear(): void; +} + +export enum CompileStrategy { + ABC, + EXTERNAL, + ABC_WTIH_EXTERNAL, +} diff --git a/arkui-plugins/tsconfig.build.json b/arkui-plugins/tsconfig.build.json index 02074ad930560974d147ac73dbf42454b362dd2f..a7e077cb8bed88d599bd46169b02949a61948b82 100644 --- a/arkui-plugins/tsconfig.build.json +++ b/arkui-plugins/tsconfig.build.json @@ -17,6 +17,7 @@ "removeComments": false }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/tsconfig.json b/arkui-plugins/tsconfig.json index 32677ce448cc2c8909203b25292cb602a457f471..5ea0297ec1a3b349662239293f9eed0f506dae0e 100644 --- a/arkui-plugins/tsconfig.json +++ b/arkui-plugins/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "es2018", + "target": "esnext", "lib": ["ESNext", "ESNext.WeakRef", "DOM"], - "module": "CommonJS", + "module": "esnext", "rootDir": ".", "baseUrl": ".", "outDir": "./lib", @@ -16,9 +16,11 @@ "strict": true, "skipLibCheck": true, "removeComments": false, - "isolatedModules": true + "isolatedModules": true, + "esModuleInterop": true }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts new file mode 100644 index 0000000000000000000000000000000000000000..4cd6d774a44df206a1403fed130665abf7f3caa5 --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-factory.ts @@ -0,0 +1,122 @@ +/* + * Copyright (c) 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 { ConditionScopeVisitor } from './condition-scope-visitor'; +import { coerceToAstNode } from '../../common/arkts-utils'; +import { factory as UIFactory } from '../ui-factory'; + +export class BuilderFactory { + /** + * rewrite `@Builder` function body with `ConditionScopeVisitor`. + * + * @internal + */ + static rewriteBuilderScriptFunction(node: T): arkts.ScriptFunction { + const _node = coerceToAstNode(node); + let funcBody: arkts.AstNode | undefined = _node.body; + if (!funcBody || !arkts.isBlockStatement(funcBody)) { + return _node; + } + const conditionScopeVisitor = ConditionScopeVisitor.getInstance(); + funcBody = arkts.factory.updateBlock( + funcBody, + funcBody.statements.map((st) => { + const newNode = conditionScopeVisitor.visitor(st); + conditionScopeVisitor.reset(); + return newNode; + }) + ); + return UIFactory.updateScriptFunction(_node, { body: funcBody }); + } + + /** + * rewrite `@Builder` method. + */ + static rewriteBuilderMethod(node: T): arkts.MethodDefinition { + const _node = coerceToAstNode(node); + const newFunc = BuilderFactory.rewriteBuilderScriptFunction(_node.scriptFunction); + return arkts.factory.updateMethodDefinition(_node, _node.kind, _node.name, newFunc, _node.modifiers, false); + } + + /** + * rewrite `@Builder` class property with arrow function value. + */ + static rewirteBuilderClassProperty(node: T): arkts.ClassProperty { + const _node = coerceToAstNode(node); + const value = _node.value; + if (!value || !arkts.isArrowFunctionExpression(value)) { + return _node; + } + const newValue = BuilderFactory.rewriteBuilderArrowFunction(value); + return arkts.factory.updateClassProperty( + _node, + _node.key, + newValue, + _node.typeAnnotation, + _node.modifiers, + false + ); + } + + /** + * rewrite `@Builder` property with arrow function value. + */ + static rewriteBuilderProperty(node: T): arkts.Property { + const _node = coerceToAstNode(node); + const value = _node.value; + if (!value || !arkts.isArrowFunctionExpression(value)) { + return _node; + } + const newValue = BuilderFactory.rewriteBuilderArrowFunction(value); + return arkts.factory.updateProperty(_node, _node.key, newValue); + } + + /** + * rewrite `@Builder` arrow function. + */ + static rewriteBuilderArrowFunction( + node: T + ): arkts.ArrowFunctionExpression { + const _node = coerceToAstNode(node); + const newFunc = BuilderFactory.rewriteBuilderScriptFunction(_node.scriptFunction); + return arkts.factory.updateArrowFunction(_node, newFunc); + } + + /** + * rewrite `@Builder` parameter with arrow function value. + */ + static rewriteBuilderParameter( + node: T + ): arkts.ETSParameterExpression { + const _node = coerceToAstNode(node); + const initializer = _node.initializer; + if (!initializer || !arkts.isArrowFunctionExpression(initializer)) { + return _node; + } + const newInitializer = BuilderFactory.rewriteBuilderArrowFunction(initializer); + return arkts.factory.updateParameterDeclaration(_node, _node.identifier, newInitializer); + } +} + +export type BuilderRewriteFn = (node: T) => T; + +export const builderRewriteByType = new Map>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, BuilderFactory.rewriteBuilderMethod], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, BuilderFactory.rewirteBuilderClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, BuilderFactory.rewriteBuilderProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, BuilderFactory.rewriteBuilderArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, BuilderFactory.rewriteBuilderParameter], +]); diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts index 5e4dc8331a4a365a1caded9ab0e231eb4cb37d2d..aba68967cd55f7aa67be5962fa72a91821f137ad 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts @@ -14,12 +14,22 @@ */ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; +import { ImportCollector } from '../../common/import-collector'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './utils'; import { factory } from './factory'; +import { ProjectConfig } from '../../common/plugin-context'; export class BuilderLambdaTransformer extends AbstractVisitor { + projectConfig: ProjectConfig | undefined; + + constructor(projectConfig: ProjectConfig | undefined) { + super(); + this.projectConfig = projectConfig; + } + reset(): void { super.reset(); + ImportCollector.getInstance().reset(); } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { @@ -32,6 +42,9 @@ export class BuilderLambdaTransformer extends AbstractVisitor { return this.visitEachChild(lambda); } const node = this.visitEachChild(beforeChildren); + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + } return node; } } diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts new file mode 100644 index 0000000000000000000000000000000000000000..693053ba845fb8e44250f622dcf3ddf1d8a9c3a2 --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/cache/conditionBreakCache.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 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'; + +export class ConditionBreakCache { + private _shouldBreak: boolean; + private _shouldReturn: boolean; + private _shouldContinue: boolean; + private static instance: ConditionBreakCache | null = null; + + private constructor() { + this._shouldBreak = false; + this._shouldReturn = false; + this._shouldContinue = false; + } + + static getInstance(): ConditionBreakCache { + if (!this.instance) { + this.instance = new ConditionBreakCache(); + } + return this.instance; + } + + get shouldBreak(): boolean { + return this._shouldBreak; + } + + get shouldReturn(): boolean { + return this._shouldReturn; + } + + get shouldContinue(): boolean { + return this._shouldContinue; + } + + collectBreak(): void { + if (this._shouldBreak) { + return; + } + this._shouldBreak = true; + } + + collectReturn(): void { + if (this._shouldReturn) { + return; + } + this._shouldReturn = true; + } + + collectContinue(): void { + if (this._shouldContinue) { + return; + } + this._shouldContinue = true; + } + + collect(st: arkts.AstNode): boolean { + if (arkts.isBreakStatement(st)) { + this.collectBreak(); + return true; + } + if (arkts.isReturnStatement(st)) { + this.collectReturn(); + return true; + } + if (arkts.isContinueStatement(st)) { + this.collectContinue(); + return true; + } + return false; + } + + reset(): void { + this._shouldBreak = false; + this._shouldReturn = false; + this._shouldContinue = false; + } +} diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..86ec83025b010cc0f9112fb8adadfd67b0c51b02 --- /dev/null +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/condition-scope-visitor.ts @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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 } from '../../common/abstract-visitor'; +import { factory as BuilderLambdaFactory } from './factory'; + +/** + * `ConditionScopeVisitor` is used to visit `@Builder` function body to wrap `ConditionScope`/`ConditionBranch` + * to if-else or switch-case statements. + * + * @internal + */ +export class ConditionScopeVisitor extends AbstractVisitor { + private static instance: ConditionScopeVisitor; + private _enforceUpdateCondition: boolean = false; + private _shouldUpdateCondition: boolean = true; + + private constructor() { + super(); + } + + private get shouldUpdateCondition(): boolean { + if (this._enforceUpdateCondition) { + return true; + } + return this._shouldUpdateCondition; + } + + private set shouldUpdateCondition(newValue: boolean) { + if (this._enforceUpdateCondition) { + this._shouldUpdateCondition = true; + return; + } + this._shouldUpdateCondition = newValue; + } + + static getInstance(): ConditionScopeVisitor { + if (!this.instance) { + this.instance = new ConditionScopeVisitor(); + } + return this.instance; + } + + private enter(node: arkts.AstNode): void { + if (arkts.isVariableDeclarator(node) && arkts.NodeCache.getInstance().has(node)) { + this._enforceUpdateCondition = true; + } + } + + reset(): void { + this._enforceUpdateCondition = false; + this._shouldUpdateCondition = true; + } + + visitor(node: arkts.AstNode): arkts.AstNode { + this.enter(node); + if (arkts.isIfStatement(node) && this.shouldUpdateCondition) { + return BuilderLambdaFactory.updateIfElseContentBodyInBuilderLambda(node, true, true); + } + if (arkts.isSwitchStatement(node) && this.shouldUpdateCondition) { + return BuilderLambdaFactory.updateSwitchCaseContentBodyInBuilderLambda(node, true, true); + } + if (arkts.isBlockStatement(node) && this.shouldUpdateCondition) { + const newStatements = node.statements.map((st) => + BuilderLambdaFactory.updateContentBodyInBuilderLambda(st, true, true) + ); + return arkts.factory.updateBlock(node, newStatements); + } + if ( + arkts.isArrowFunctionExpression(node) && + !arkts.NodeCache.getInstance().has(node) && + !arkts.NodeCache.getInstance().has(node.scriptFunction) + ) { + this.shouldUpdateCondition = false; + this._enforceUpdateCondition = false; + } + return this.visitEachChild(node); + } +} diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 9c303f7659da18e823426653cff0299052e4a535..dd3a647aaf3ea2ee19e8349cc58e52fc8a0284f6 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -15,49 +15,78 @@ import * as arkts from '@koalaui/libarkts'; import { BuilderLambdaNames } from '../utils'; -import { annotation, backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; +import { + backingField, + filterDefined, + isDecoratorAnnotation, + removeAnnotationByName, + forEachArgWithParam, + annotation, +} from '../../common/arkts-utils'; import { BuilderLambdaDeclInfo, builderLambdaFunctionName, builderLambdaMethodDeclType, - builderLambdaTypeName, callIsGoodForBuilderLambda, + collectComponentAttributeImport, findBuilderLambdaDecl, findBuilderLambdaDeclInfo, isBuilderLambda, isBuilderLambdaFunctionCall, isSafeType, replaceBuilderLambdaDeclMethodName, - BindableDecl, - getDecalTypeFromValue, - hasBindableProperty, isDoubleDollarCall, InstanceCallInfo, isStyleChainedCall, isStyleWithReceiverCall, + builderLambdaType, + BuilderLambdaSecondLastArgInfo, + buildSecondLastArgInfo, + checkIsWithInIfConditionScope, + BuilderLambdaConditionBranchInfo, + BuilderLambdaChainingCallArgInfo, + getArgumentType, } from './utils'; -import { DecoratorNames } 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 { factory as UIFactory } from '../ui-factory'; +import { + AnimationNames, + ARKUI_BUILDER_SOURCE_NAME, + BindableDecl, + ConditionNames, + DecoratorIntrinsicNames, + DecoratorNames, +} from '../../common/predefines'; +import { ImportCollector } from '../../common/import-collector'; +import { addMemoAnnotation, collectMemoableInfoInParameter } from '../../collectors/memo-collectors/utils'; +import { factory as MemoCollectFactory } from '../../collectors/memo-collectors/factory'; +import { BuilderFactory } from './builder-factory'; +import { ConditionBreakCache } from './cache/conditionBreakCache'; export class factory { - /* - * update @ComponentBuilder decorated method. + /** + * update `@ComponentBuilder` decorated method. */ static updateBuilderLambdaMethodDecl( node: arkts.MethodDefinition, - styleArg: arkts.ETSParameterExpression, + prefixArgs: arkts.ETSParameterExpression[], newAnno: arkts.AnnotationUsage[], - newName: string | undefined + newName: string | undefined, + externalSourceName?: string ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; + let newParams: arkts.Expression[] = []; + if (func.params.length > 0) { + newParams.push(...prefixArgs, ...func.params); + } const updateFunc = arkts.factory .updateScriptFunction( func, func.body, arkts.FunctionSignature.createFunctionSignature( func.typeParams, - [styleArg, ...func.params], + newParams, arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -70,40 +99,50 @@ 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 // TODO: how do I get it? + false ); } /* * transform arguments in style node. */ - static getTransformedStyle(call: arkts.CallExpression): arkts.Expression[] { + static getTransformedStyle(call: arkts.CallExpression): BuilderLambdaChainingCallArgInfo[] { const decl = arkts.getDecl(call.expression); if (!decl || !arkts.isMethodDefinition(decl)) { - return [...call.arguments]; - } - const type: arkts.AstNode | undefined = arkts.isEtsParameterExpression(decl.scriptFunction.params[0]) - ? decl.scriptFunction.params[0].type - : undefined; - if ( - type && - arkts.isTypeNode(type) && - hasBindableProperty(type, BindableDecl.BINDABLE) && - isDoubleDollarCall(call.arguments[0]) - ) { - const bindableArg: arkts.Expression = (call.arguments[0] as arkts.CallExpression).arguments[0]; - return [factory.updateBindableStyleArguments(bindableArg), ...call.arguments.slice(1)]; + return call.arguments.map((arg) => ({ arg })); } - return [...call.arguments]; + const argInfo: BuilderLambdaChainingCallArgInfo[] = []; + const args = call.arguments; + const params = decl.scriptFunction.params; + const isTrailingCall = call.isTrailingCall; + forEachArgWithParam( + args, + params, + (arg, param, index) => { + const _param = param as arkts.ETSParameterExpression; + let isDoubleDollar: boolean = false; + if (index === 0 && !!arg) { + isDoubleDollar = isDoubleDollarCall(arg); + } + if (isDoubleDollar && !!arg) { + const bindableArg: arkts.Expression = (arg as arkts.CallExpression).arguments[0]; + argInfo.push({ arg: factory.updateBindableStyleArguments(bindableArg) }); + } else if (!!arg) { + argInfo.push({ arg, hasBuilder: hasDecorator(_param, DecoratorNames.BUILDER) }); + } + }, + { isTrailingCall } + ); + return argInfo; } /* * transform bundable arguments in style node, e.g. `Radio().checked($$(this.checked))` => `Radio().checked({value: xxx, onChange: xxx})`. */ static updateBindableStyleArguments(bindableArg: arkts.Expression): arkts.Expression { - const valueType: arkts.TypeNode = getDecalTypeFromValue(bindableArg); + const valueType: arkts.TypeNode = getArgumentType(bindableArg); const objExp: arkts.ObjectExpression = arkts.factory.createObjectExpression( arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, [factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType)], @@ -115,9 +154,9 @@ export class factory { /* * create style instance call, e.g. `instance.margin(10)`. */ - static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo, projectConfig: ProjectConfig | undefined): arkts.CallExpression { + static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo): arkts.CallExpression { if (!callInfo.isReceiver) { - const newArgs: arkts.Expression[] = factory.getTransformedStyle(callInfo.call); + const argInfos: BuilderLambdaChainingCallArgInfo[] = factory.getTransformedStyle(callInfo.call); return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( lambdaBody, @@ -127,11 +166,11 @@ export class factory { false ), undefined, - newArgs.map((arg) => { - if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + argInfos.map((info) => { + if (arkts.isArrowFunctionExpression(info.arg)) { + return this.processArgArrowFunction(info.arg, info.hasBuilder); } - return arg; + return info.arg; }) ); } else { @@ -161,20 +200,25 @@ export class factory { */ static createStyleArgInBuilderLambda( lambdaBody: arkts.Expression | undefined, - typeNode: arkts.TypeNode | undefined + typeNode: arkts.TypeNode | undefined, + moduleName: string ): arkts.UndefinedLiteral | arkts.ArrowFunctionExpression { if (!lambdaBody) { return arkts.factory.createUndefinedLiteral(); } + collectComponentAttributeImport(typeNode, moduleName); const safeType: arkts.TypeNode | undefined = isSafeType(typeNode) ? typeNode : undefined; + const styleLambdaParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME, safeType), undefined ); + const returnStatement = arkts.factory.createReturnStatement(); + arkts.NodeCache.getInstance().collect(returnStatement); const body: arkts.BlockStatement = arkts.factory.createBlock([ arkts.factory.createExpressionStatement(lambdaBody), - arkts.factory.createReturnStatement(), + returnStatement, ]); const func = arkts.factory.createScriptFunction( @@ -189,7 +233,7 @@ export class factory { arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - return arkts.factory.createArrowFunction(func); + return addMemoAnnotation(arkts.factory.createArrowFunction(func)); } /* @@ -212,23 +256,15 @@ export class factory { ), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW ); + addMemoAnnotation(funcType); let parameter: arkts.ETSParameterExpression; - if (isFunctionCall) { - parameter = arkts.factory - .createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, funcType), - undefined - ) - .setOptional(true); - } else { - const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); - parameter = arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), - undefined - ); - } - parameter.annotations = [annotation('memo')]; + const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); + parameter = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), + undefined + ); + arkts.NodeCache.getInstance().collect(parameter); return parameter; } @@ -236,15 +272,18 @@ export class factory { * If a builder lambda's argument is an arrow function, * then transform any builder lambda in the function body. */ - static processArgArrowFunction(arg: arkts.ArrowFunctionExpression, projectConfig: ProjectConfig | undefined): arkts.ArrowFunctionExpression { + static processArgArrowFunction( + arg: arkts.ArrowFunctionExpression, + hasBuilder?: boolean + ): arkts.ArrowFunctionExpression { const func: arkts.ScriptFunction = arg.scriptFunction; const updateFunc = arkts.factory.updateScriptFunction( func, !!func.body && arkts.isBlockStatement(func.body) ? arkts.factory.updateBlock( - func.body, - func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) - ) + func.body, + func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, hasBuilder)) + ) : undefined, arkts.FunctionSignature.createFunctionSignature( func.typeParams, @@ -268,15 +307,16 @@ export class factory { } else if (arkts.isObjectExpression(arg)) { expr = arg; } - if (!expr) { return arg; } - - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName!); - const properties = expr.properties as arkts.Property[]; - properties.forEach((prop, index) => { - this.updateParameterPassingInLinkedProperties(prop, index, currentStructInfo, properties); + const properties = (expr.properties as arkts.Property[]).map((p) => { + let property = p; + MemoCollectFactory.findAndCollectMemoableProperty(p, (currNode: arkts.Property) => { + property = BuilderFactory.rewriteBuilderProperty(currNode); + return property; + }); + return factory.updatePropertiesInOptions(property); }); const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( expr, @@ -290,49 +330,37 @@ export class factory { return updatedExpr as T; } - /** - * update any `@Link` parameter passing to the custom-component child. - */ - static updateParameterPassingInLinkedProperties( - prop: arkts.Property, - index: number, - currentStructInfo: arkts.StructInfo, - properties: arkts.Property[] - ): void { - const decl = prop.key ? arkts.getDecl(prop.key) : undefined; - if (decl && arkts.isMethodDefinition(decl)) { - const type: arkts.TypeNode | undefined = decl.scriptFunction.returnTypeAnnotation; - if ( - type && - hasBindableProperty(type, BindableDecl.BINDABLE) && - arkts.isProperty(prop) && - prop.value && - isDoubleDollarCall(prop.value) - ) { - properties[index] = factory.updateBindableProperty(prop); - } + static updatePropertiesInOptions(prop: arkts.Property): arkts.Property { + let decl: arkts.AstNode | undefined; + if (!prop.key || !prop.value || !(decl = arkts.getDecl(prop.key)) || !arkts.isMethodDefinition(decl)) { + return prop; } - if ( - !!prop.key && - !!prop.value && + let isBuilderParam: boolean = false; + let isLinkIntrinsic: boolean = false; + decl.scriptFunction.annotations.forEach((anno) => { + isBuilderParam ||= isDecoratorAnnotation(anno, DecoratorNames.BUILDER_PARAM); + isLinkIntrinsic ||= isDecoratorIntrinsicAnnotation(anno, DecoratorIntrinsicNames.LINK); + }); + + if (isDoubleDollarCall(prop.value)) { + return factory.updateBindableProperty(prop); + } else if (isBuilderParam && arkts.isArrowFunctionExpression(prop.value)) { + addMemoAnnotation(prop.value); + return prop; + } else if ( + isLinkIntrinsic && arkts.isIdentifier(prop.key) && arkts.isMemberExpression(prop.value) && arkts.isThisExpression(prop.value.object) && arkts.isIdentifier(prop.value.property) ) { - const structVariableMetadata = currentStructInfo.metadata[prop.key.name]; - if ( - structVariableMetadata && - structVariableMetadata.properties.length && - structVariableMetadata.properties.includes(DecoratorNames.LINK) - ) { - properties[index] = arkts.Property.updateProperty( - prop, - arkts.factory.createIdentifier(backingField(prop.key.name)), - this.updateBackingMember(prop.value, prop.value.property.name) - ); - } + return arkts.Property.updateProperty( + prop, + arkts.factory.createIdentifier(backingField(prop.key.name)), + factory.updateBackingMember(prop.value, prop.value.property.name) + ); } + return prop; } /** @@ -340,15 +368,20 @@ export class factory { * If the corresponding argument is not provided, fill-in an `undefined` to it. */ static createOrUpdateArgInBuilderLambda( + fallback: arkts.AstNode | undefined, arg: arkts.Expression | undefined, - projectConfig: ProjectConfig | undefined, typeName?: string, - ): arkts.AstNode { + canAddMemo?: boolean + ): arkts.AstNode | undefined { if (!arg) { - return arkts.factory.createUndefinedLiteral(); + return fallback; } if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + const newNode = this.processArgArrowFunction(arg, canAddMemo); + if (canAddMemo) { + addMemoAnnotation(newNode); + } + return newNode; } // this is too optimistic to check if this is an options argument... if (arkts.isTSAsExpression(arg) || arkts.isObjectExpression(arg)) { @@ -357,76 +390,221 @@ export class factory { return arg; } + static createSecondLastArgInBuilderLambda(argInfo: BuilderLambdaSecondLastArgInfo): arkts.AstNode | undefined { + if (!!argInfo.isReusable && !!argInfo.reuseId) { + const reuseIdNode = arkts.factory.createStringLiteral(argInfo.reuseId); + return this.createOrUpdateArgInBuilderLambda(reuseIdNode, undefined, undefined); + } else if (!argInfo.isFunctionCall) { + return this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), undefined, undefined); + } + return undefined; + } + /** * transform arguments in a builder lambda call. */ static generateArgsInBuilderLambda( leaf: arkts.CallExpression, lambdaBody: arkts.Identifier | arkts.CallExpression, - declInfo: BuilderLambdaDeclInfo, - projectConfig: ProjectConfig | undefined + declInfo: BuilderLambdaDeclInfo ): (arkts.AstNode | undefined)[] { - const { params, returnType } = declInfo; - const typeName: string | undefined = builderLambdaTypeName(leaf); - const args: (arkts.AstNode | undefined)[] = [this.createStyleArgInBuilderLambda(lambdaBody, returnType)]; - let index = 0; - while (index < params.length) { - args.push(this.createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), projectConfig, typeName)); - index++; - } - const isReusable: boolean = typeName - ? arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName).isReusable : false; - if (isReusable) { - args.splice(-1, 1, arkts.factory.createStringLiteral(typeName!)); - } - else if (typeName === 'XComponent') { - let packageInfo: string = ''; - if (projectConfig?.bundleName && projectConfig?.moduleName) { - packageInfo = projectConfig?.bundleName + '/' + projectConfig?.moduleName; - } - args.splice(args.length - 1, 0, arkts.factory.createStringLiteral(packageInfo)); - } - return args; + const { isFunctionCall, params, returnType, moduleName } = declInfo; + const type: arkts.Identifier | undefined = builderLambdaType(leaf); + const args: (arkts.AstNode | undefined)[] = [ + this.createStyleArgInBuilderLambda(lambdaBody, returnType, moduleName), + ]; + const secondLastArgInfo = buildSecondLastArgInfo(type, isFunctionCall); + const isTrailingCall = leaf.isTrailingCall; + forEachArgWithParam( + leaf.arguments, + params, + (arg, param, index) => { + let modifiedArg: arkts.AstNode | undefined; + if (index === params.length - 2 && !arg) { + modifiedArg = this.createSecondLastArgInBuilderLambda(secondLastArgInfo); + } + if (!modifiedArg) { + const memoableInfo = collectMemoableInfoInParameter(param); + const canAddMemo = + (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + modifiedArg = this.createOrUpdateArgInBuilderLambda( + arkts.factory.createUndefinedLiteral(), + arg, + type?.name, + canAddMemo + ); + } + args.push(modifiedArg); + }, + { isTrailingCall } + ); + return filterDefined(args); } /** - * update if-else in trailing lambda contents in a builder lambda call. + * update if-else in a builder lambda call's arguments. */ - static updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode, projectConfig: ProjectConfig | undefined): arkts.AstNode { + static updateIfElseContentBodyInBuilderLambda( + statement: arkts.AstNode, + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): arkts.AstNode { if (arkts.isIfStatement(statement)) { const alternate = !!statement.alternate - ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, projectConfig) + ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, shouldWrap, stopAtBuilderLambda) : statement.alternate; - const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent, projectConfig); - return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); + const consequence = this.updateIfElseContentBodyInBuilderLambda( + statement.consequent, + shouldWrap, + stopAtBuilderLambda + ); + const newStatement = arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); + return !shouldWrap || checkIsWithInIfConditionScope(statement) + ? newStatement + : this.wrapConditionToBlock([newStatement], ConditionNames.CONDITION_SCOPE); } if (arkts.isBlockStatement(statement)) { - return arkts.factory.updateBlock( - statement, - statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) + let { statements, breakIndex } = this.updateConditionBranchInScope( + statement.statements, + shouldWrap, + stopAtBuilderLambda + ); + if (!!shouldWrap && checkIsWithInIfConditionScope(statement)) { + const beforeBreak = this.wrapConditionToBlock( + breakIndex > 0 ? statements.slice(0, breakIndex) : statements, + ConditionNames.CONDITION_BRANCH + ); + const afterBreak = breakIndex > 0 ? statements.slice(breakIndex) : []; + statements = [beforeBreak, ...afterBreak]; + } + return arkts.factory.updateBlock(statement, statements); + } + return statement; + } + + /** + * update switch-case in a builder lambda call's arguments. + */ + static updateSwitchCaseContentBodyInBuilderLambda( + statement: T, + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): T { + if (arkts.isSwitchStatement(statement)) { + const cases = statement.cases.map((c) => + this.updateSwitchCaseContentBodyInBuilderLambda(c, shouldWrap, stopAtBuilderLambda) ); + const newStatement = arkts.factory.updateSwitchStatement(statement, statement.discriminant, cases); + return (!shouldWrap + ? newStatement + : this.wrapConditionToBlock([newStatement], ConditionNames.CONDITION_SCOPE)) as arkts.AstNode as T; + } + if (arkts.isSwitchCaseStatement(statement)) { + let { statements, breakIndex } = this.updateConditionBranchInScope(statement.consequent, shouldWrap); + if (shouldWrap && breakIndex > 0) { + const hasBreak = breakIndex !== statements.length; + const beforeBreak = this.wrapConditionToBlock( + statements.slice(0, breakIndex), + ConditionNames.CONDITION_BRANCH + ); + const afterBreak = statements.slice(hasBreak ? breakIndex + 1 : breakIndex); + const breakStatement = this.createBreakBetweenConditionStatements(); + statements = [beforeBreak, ...breakStatement, ...afterBreak]; + } + ConditionBreakCache.getInstance().reset(); + return arkts.factory.updateSwitchCaseStatement(statement, statement.test, statements) as T; } return statement; } + static createBreakBetweenConditionStatements(): arkts.AstNode[] { + const cache = ConditionBreakCache.getInstance(); + if (cache.shouldBreak) { + return [arkts.factory.createBreakStatement()]; + } + if (cache.shouldReturn) { + return [arkts.factory.createReturnStatement()]; + } + return []; + } + + /** + * update ConditionBranch in an if-else or swith-case body. + * @internal + */ + static updateConditionBranchInScope( + statements: readonly arkts.Statement[], + shouldWrap?: boolean, + stopAtBuilderLambda?: boolean + ): BuilderLambdaConditionBranchInfo { + let breakIndex = statements.length; + const newStatements = statements.map((st, index) => { + if (ConditionBreakCache.getInstance().collect(st)) { + breakIndex = index; + } + return this.updateContentBodyInBuilderLambda(st, shouldWrap, stopAtBuilderLambda); + }); + return { statements: newStatements, breakIndex }; + } + + /** + * wrap `ConditionScope` or `ConditionBranch` builder function to the block statements. + */ + static wrapConditionToBlock(statements: readonly arkts.AstNode[], condition: ConditionNames): arkts.AstNode { + const contentArg = arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock(statements), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ); + addMemoAnnotation(contentArg); + const newCall = arkts.factory.createCallExpression(arkts.factory.createIdentifier(condition), undefined, [ + contentArg, + ]); + arkts.NodeCache.getInstance().collect(newCall); + ImportCollector.getInstance().collectSource(condition, ARKUI_BUILDER_SOURCE_NAME); + ImportCollector.getInstance().collectImport(condition); + return arkts.factory.createExpressionStatement(newCall); + } + /** * update trailing lambda contents in a builder lambda call. */ - static updateContentBodyInBuilderLambda(statement: arkts.Statement, projectConfig: ProjectConfig | undefined): arkts.Statement { + static updateContentBodyInBuilderLambda( + statement: arkts.Statement, + hasBuilder?: boolean, + stopAtBuilderLambda?: boolean + ): arkts.Statement { if ( arkts.isExpressionStatement(statement) && arkts.isCallExpression(statement.expression) && isBuilderLambda(statement.expression) ) { + if (!!stopAtBuilderLambda) { + return statement; + } return arkts.factory.updateExpressionStatement( statement, - this.transformBuilderLambda(statement.expression, projectConfig) + this.transformBuilderLambda(statement.expression) ); } if (arkts.isIfStatement(statement)) { - return this.updateIfElseContentBodyInBuilderLambda(statement, projectConfig); + return this.updateIfElseContentBodyInBuilderLambda(statement, hasBuilder, stopAtBuilderLambda); + } + if (arkts.isSwitchStatement(statement)) { + return this.updateSwitchCaseContentBodyInBuilderLambda(statement, hasBuilder, stopAtBuilderLambda); + } + if (arkts.isBlockStatement(statement)) { + const newStatements = statement.statements.map((st) => + this.updateContentBodyInBuilderLambda(st, hasBuilder, stopAtBuilderLambda) + ); + return arkts.factory.updateBlock(statement, newStatements); + } + if (arkts.isBreakStatement(statement) && statement.parent && arkts.isBlockStatement(statement.parent)) { + ConditionBreakCache.getInstance().collectBreak(); + return arkts.factory.createReturnStatement(); } - return statement; } @@ -460,21 +638,26 @@ export class factory { /** * transform `@ComponentBuilder` in declared methods. */ - static transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.MethodDefinition { + static transformBuilderLambdaMethodDecl( + node: arkts.MethodDefinition, + externalSourceName?: string + ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); const typeNode: arkts.TypeNode | undefined = builderLambdaMethodDeclType(node); - const styleArg: arkts.ETSParameterExpression = this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall); const newOverloads: arkts.MethodDefinition[] = node.overloads.map((method) => factory.transformBuilderLambdaMethodDecl(method) ); - return this.updateBuilderLambdaMethodDecl( + const newNode = this.updateBuilderLambdaMethodDecl( node, - styleArg, + [this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall)], removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), - replaceBuilderLambdaDeclMethodName(node.name.name) + replaceBuilderLambdaDeclMethodName(node.name.name), + externalSourceName ).setOverloads(newOverloads); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /** @@ -490,14 +673,14 @@ export class factory { continue; } const property: arkts.Identifier = instanceCalls[curIdx].call.expression as arkts.Identifier; - if (property.name === BuilderLambdaNames.ANIMATION_NAME) { + if (property.name === AnimationNames.ANIMATION) { const aniStart: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_START), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_START), undefined, instanceCalls[curIdx].call.arguments ); const aniStop: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_STOP), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_STOP), undefined, instanceCalls[curIdx].call.arguments.map((arg) => arg.clone()) ); @@ -514,7 +697,7 @@ export class factory { /** * transform `@ComponentBuilder` in non-declared calls. */ - static transformBuilderLambda(node: arkts.CallExpression, projectConfig: ProjectConfig | undefined): arkts.AstNode { + static transformBuilderLambda(node: arkts.CallExpression): arkts.AstNode { let instanceCalls: InstanceCallInfo[] = []; let leaf: arkts.CallExpression = node; @@ -556,13 +739,16 @@ export class factory { instanceCalls = instanceCalls.reverse(); this.updateAnimation(instanceCalls); lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); + arkts.NodeCache.getInstance().collect(lambdaBody); instanceCalls.forEach((callInfo) => { - lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo, projectConfig); + lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo); }); } - const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo, projectConfig); - return arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo); + const newNode = arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /* @@ -578,7 +764,7 @@ export class factory { prop.value.arguments.length === 1 ) { let bindableArg = prop.value.arguments[0]; - valueType = getDecalTypeFromValue(bindableArg); + valueType = getArgumentType(bindableArg); res.push( factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType) @@ -638,11 +824,76 @@ export class factory { * generate `Bindable`. */ static createBindableType(valueType: arkts.TypeNode): arkts.ETSTypeReference { + const transformedKey = BindableDecl.BINDABLE; + ImportCollector.getInstance().collectImport(transformedKey); return arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(BindableDecl.BINDABLE), + arkts.factory.createIdentifier(transformedKey), arkts.factory.createTSTypeParameterInstantiation([valueType.clone()]) ) ); } + + /** + * create a `@Builder` function with `@Builder` trailing lambda parameter. + */ + static createBuilderWithTrailingLambdaDecl( + name: string, + returnTypeAnnotation: arkts.TypeNode + ): arkts.MethodDefinition { + const key = arkts.factory.createIdentifier(name); + const modifiers = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + const param = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + BuilderLambdaNames.CONTENT_PARAM_NAME, + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + param.annotations = [annotation(DecoratorNames.BUILDER)]; + arkts.NodeCache.getInstance().collect(param); + const method = UIFactory.createMethodDefinition({ + key, + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_NONE, + function: { + key, + params: [param], + returnTypeAnnotation, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_NONE, + modifiers, + annotations: [annotation(DecoratorNames.BUILDER)], + }, + modifiers, + }); + arkts.NodeCache.getInstance().collect(method); + return method; + } + + /** + * add following declared methods at header file: + * - `@Builder function ConditionScope(@Builder content: () => void): void;` + * - `@Builder function ConditionBranch(@Builder content: () => void): void;` + */ + static addConditionBuilderDecls(): arkts.MethodDefinition[] { + const conditionScope = this.createBuilderWithTrailingLambdaDecl( + ConditionNames.CONDITION_SCOPE, + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ); + const conditionBranch = this.createBuilderWithTrailingLambdaDecl( + ConditionNames.CONDITION_BRANCH, + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ); + return [conditionScope, conditionBranch]; + } } diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index f7cd7c6de5efde0ab140b172985a083a8641c13e..1d5d3737c24201564be6c7ac6d61ad3f41b8c13c 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -14,19 +14,19 @@ */ import * as arkts from '@koalaui/libarkts'; -import { isAnnotation } from '../../common/arkts-utils'; -import { BuilderLambdaNames, Dollars } from '../utils'; +import { isAnnotation, matchPrefix } from '../../common/arkts-utils'; +import { BuilderLambdaNames, isCustomComponentAnnotation } from '../utils'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, BindableDecl, Dollars, StructDecoratorNames } from '../../common/predefines'; +import { ImportCollector } from '../../common/import-collector'; export type BuilderLambdaDeclInfo = { isFunctionCall: boolean; // isFunctionCall means it is from $_instantiate. params: readonly arkts.Expression[]; returnType: arkts.TypeNode | undefined; + moduleName: string; }; -export enum BindableDecl { - BINDABLE = 'Bindable', -} - export type BuilderLambdaAstNode = arkts.ScriptFunction | arkts.ETSParameterExpression | arkts.FunctionDeclaration; export type InstanceCallInfo = { @@ -34,6 +34,46 @@ export type InstanceCallInfo = { call: arkts.CallExpression; }; +export type BuilderLambdaArgInfo = { + isFunctionCall: boolean; +}; + +export type BuilderLambdaReusableArgInfo = { + isReusable?: boolean; + reuseId?: string; +}; + +export type BuilderLambdaSecondLastArgInfo = BuilderLambdaArgInfo & BuilderLambdaReusableArgInfo; + +export type BuilderLambdaConditionBranchInfo = { + statements: readonly arkts.Statement[]; + breakIndex: number; +}; + +export type BuilderLambdaChainingCallArgInfo = { + arg: arkts.Expression; + hasBuilder?: boolean; +}; + +export function buildSecondLastArgInfo( + type: arkts.Identifier | undefined, + isFunctionCall: boolean +): BuilderLambdaSecondLastArgInfo { + let isReusable: boolean | undefined; + let reuseId: string | undefined; + if (!isFunctionCall && !!type) { + const customComponentDecl = arkts.getDecl(type); + isReusable = + !!customComponentDecl && + arkts.isClassDefinition(customComponentDecl) && + customComponentDecl.annotations.some((anno) => + isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE) + ); + reuseId = isReusable ? type.name : undefined; + } + return { isFunctionCall, isReusable, reuseId }; +} + /** * Used in finding "XXX" in BuilderLambda("XXX") * @deprecated @@ -54,13 +94,8 @@ export function builderLambdaArgumentName(annotation: arkts.AnnotationUsage): st return property.value.str; } -/** - * Determine whether it is a custom component. - * - * @param node class declaration node - */ -export function isBuilderLambda(node: arkts.AstNode, isExternal?: boolean): boolean { - const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node); +export function isBuilderLambda(node: arkts.AstNode, nodeDecl?: arkts.AstNode | undefined): boolean { + const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node, nodeDecl); if (!builderLambdaCall) { return arkts.isCallExpression(node) && node.arguments.length > 0 && isBuilderLambda(node.arguments[0]); } @@ -131,15 +166,12 @@ export function replaceBuilderLambdaDeclMethodName(name: string | undefined): st return undefined; } -export function isBuilderLambdaMethodDecl(node: arkts.AstNode, isExternal?: boolean): boolean { +export function isBuilderLambdaMethodDecl(node: arkts.AstNode): boolean { const builderLambdaMethodDecl: arkts.AstNode | undefined = getDeclForBuilderLambdaMethodDecl(node); return !!builderLambdaMethodDecl; } -export function getDeclForBuilderLambdaMethodDecl( - node: arkts.AstNode, - isExternal?: boolean -): arkts.AstNode | undefined { +export function getDeclForBuilderLambdaMethodDecl(node: arkts.AstNode): arkts.AstNode | undefined { if (!node || !arkts.isMethodDefinition(node)) { return undefined; } @@ -154,7 +186,10 @@ export function getDeclForBuilderLambdaMethodDecl( return undefined; } -export function getDeclForBuilderLambda(node: arkts.AstNode, isExternal?: boolean): arkts.AstNode | undefined { +export function getDeclForBuilderLambda( + node: arkts.AstNode, + nodeDecl?: arkts.AstNode | undefined +): arkts.AstNode | undefined { if (!node || !arkts.isCallExpression(node)) { return undefined; } @@ -179,15 +214,21 @@ export function getDeclForBuilderLambda(node: arkts.AstNode, isExternal?: boolea currNode = _node.object; } - if (isBuilderLambdaCall(node)) { + if (isBuilderLambdaCall(node, nodeDecl)) { return node; } return undefined; } -export function isBuilderLambdaCall(node: arkts.CallExpression | arkts.Identifier): boolean { - const expr = arkts.isIdentifier(node) ? node : node.expression; - const decl = arkts.getDecl(expr); +export function isBuilderLambdaCall( + node: arkts.CallExpression | arkts.Identifier, + nodeDecl?: arkts.AstNode | undefined +): boolean { + let decl = nodeDecl; + if (decl === undefined) { + const expr = arkts.isIdentifier(node) ? node : node.expression; + decl = arkts.getDecl(expr); + } if (!decl) { return false; @@ -278,6 +319,11 @@ export function findBuilderLambdaDecl(node: arkts.CallExpression | arkts.Identif if (!decl) { return undefined; } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName) { + return undefined; + } + DeclarationCollector.getInstance().collect(decl); return decl; } @@ -301,12 +347,15 @@ export function findBuilderLambdaDeclInfo(decl: arkts.AstNode | undefined): Buil if (!decl) { return undefined; } - + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName) { + return undefined; + } if (arkts.isMethodDefinition(decl)) { const params = decl.scriptFunction.params.map((p) => p.clone()); const returnType = decl.scriptFunction.returnTypeAnnotation?.clone(); const isFunctionCall = isBuilderLambdaFunctionCall(decl); - return { isFunctionCall, params, returnType }; + return { isFunctionCall, params, returnType, moduleName }; } return undefined; @@ -348,17 +397,17 @@ export function builderLambdaMethodDeclType(method: arkts.MethodDefinition): ark return method.scriptFunction.returnTypeAnnotation; } -export function builderLambdaTypeName(leaf: arkts.CallExpression): string | undefined { +export function builderLambdaType(leaf: arkts.CallExpression): arkts.Identifier | undefined { if (!callIsGoodForBuilderLambda(leaf)) { return undefined; } const node = leaf.expression; - let name: string | undefined; + let name: arkts.Identifier | undefined; if (arkts.isIdentifier(node)) { - name = node.name; + name = node; } if (arkts.isMemberExpression(node) && arkts.isIdentifier(node.object)) { - name = node.object.name; + name = node.object; } return name; } @@ -381,59 +430,49 @@ export function builderLambdaFunctionName(node: arkts.CallExpression): string | return undefined; } -/** - * Determine whether the node `` is `` bindable property. - * - * @param type type node - * @param bindableDecl bindable decalaration name - */ -export function hasBindableProperty(type: arkts.AstNode, bindableDecl: BindableDecl): boolean { - let res: boolean = false; - if (arkts.isETSUnionType(type)) { - type.types.forEach((item: arkts.TypeNode) => { - res = res || hasBindableProperty(item, bindableDecl); - }); - } - if (arkts.isETSTypeReference(type)) { - res = - res || - (!!type.part && - !!type.part.name && - arkts.isIdentifier(type.part.name) && - type.part.name.name === bindableDecl); - } - - return res; -} - /** * Determine whether `` is `$$()` call expression node. * * @param value expression node */ -export function isDoubleDollarCall(value: arkts.Expression): boolean { +export function isDoubleDollarCall( + value: arkts.Expression, + ignoreDecl: boolean = false +): value is arkts.CallExpression { + if (!arkts.isCallExpression(value)) { + return false; + } if ( - arkts.isCallExpression(value) && - value.expression && - arkts.isIdentifier(value.expression) && - value.expression.name + !(!!value.expression && arkts.isIdentifier(value.expression) && value.expression.name === Dollars.DOLLAR_DOLLAR) ) { - return value.expression.name === Dollars.DOLLAR_DOLLAR; + return false; } - return false; + if (!ignoreDecl) { + const decl = arkts.getDecl(value.expression); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; } /** - * get declaration type from `{xxx: }` or `fun()`. + * get declaration type from function call argument `fun()`. * - * @param value type node + * @param arg first argument in call expression. */ -export function getDecalTypeFromValue(value: arkts.Expression): arkts.TypeNode { - const decl: arkts.AstNode | undefined = arkts.getDecl(value); +export function getArgumentType(arg: arkts.Expression): arkts.TypeNode { + const isArrayElement = arkts.isMemberExpression(arg) && !!arg.property && arkts.isNumberLiteral(arg.property); + const decl: arkts.AstNode | undefined = arkts.getDecl(arg); if (!decl || !arkts.isClassProperty(decl)) { throw new Error('cannot get declaration'); } - if (isArrayType(decl.typeAnnotation!)) { + if (isArrayElement) { return getElementTypeFromArray(decl.typeAnnotation!)!; } return decl.typeAnnotation!; @@ -475,3 +514,34 @@ export function getElementTypeFromArray(arrayType: arkts.TypeNode): arkts.TypeNo } return undefined; } + +export function collectComponentAttributeImport(type: arkts.TypeNode | undefined, moduleName: string): void { + if ( + !type || + !arkts.isETSTypeReference(type) || + !type.part || + !type.part.name || + !arkts.isIdentifier(type.part.name) + ) { + return; + } + + const regex: RegExp = /(?\w+Attribute)(?:<.*>)?$/; + const name: string = type.part.name.name; + const match: RegExpExecArray | null = regex.exec(name); + const attributeName: string | undefined = match?.groups?.source; + if (!!attributeName) { + ImportCollector.getInstance().collectSource(attributeName, moduleName); + ImportCollector.getInstance().collectImport(attributeName); + } +} + +export function checkIsWithInIfConditionScope(statement: arkts.AstNode): boolean { + if (!statement.parent) { + return false; + } + if (arkts.isIfStatement(statement.parent)) { + return arkts.isBlockStatement(statement) || arkts.isIfStatement(statement); + } + return false; +} diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index eddd49be3dcbc46a045237c6deaf0c30de0563d1..ee4daa33a228c5cbb3d21bb6821ccb5c5f691031 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -17,456 +17,201 @@ import * as arkts from '@koalaui/libarkts'; import { ProjectConfig } from '../common/plugin-context'; import { factory as structFactory } from './struct-translators/factory'; import { factory as builderLambdaFactory } from './builder-lambda-translators/factory'; -import { factory as uiFactory } from './ui-factory'; import { factory as entryFactory } from './entry-translators/factory'; import { AbstractVisitor } from '../common/abstract-visitor'; -import { annotation, collect, filterDefined } from '../common/arkts-utils'; -import { - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, - getGettersFromClassDecl, - addMemoAnnotation, -} from './utils'; -import { hasDecorator, DecoratorNames } from './property-translators/utils'; +import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; +import { isEntryWrapperClass } from './entry-translators/utils'; +import { ImportCollector } from '../common/import-collector'; +import { DeclarationCollector } from '../common/declaration-collector'; +import { PropertyCache } from './property-translators/cache/propertyCache'; +import { LogCollector } from '../common/log-collector'; import { - isCustomComponentClass, - isKnownMethodDefinition, - isEtsGlobalClass, - isReourceNode, - ScopeInfoCollection, CustomComponentScopeInfo, - isMemoCall, - findCanAddMemoFromArrowFunction, + initResourceInfo, + loadBuildJson, + LoaderJson, + ResourceInfo, + ScopeInfoCollection, + isForEachDecl, } from './struct-translators/utils'; -import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; -import { isEntryWrapperClass } from './entry-translators/utils'; -import { classifyObservedTrack, classifyProperty, PropertyTranslator } from './property-translators'; -import { ObservedTrackTranslator } from './property-translators/observedTrack'; -import { nodeByType } from '@koalaui/libarkts/build/src/reexport-for-generated'; -import { isArkUICompatible, updateArkUICompatible } from './interop'; +import { + collectCustomComponentScopeInfo, + CustomComponentNames, + CustomDialogNames, + isCustomComponentClass, + isSpecificNewClass, +} from './utils'; +import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; +import { InteroperAbilityNames } from './interop/predefines'; +import { generateBuilderCompatible } from './interop/builder-interop'; +import { builderRewriteByType } from './builder-lambda-translators/builder-factory'; +import { MonitorCache } from './property-translators/cache/monitorCache'; export class CheckedTransformer extends AbstractVisitor { - private scopeInfoCollection: ScopeInfoCollection; + private scope: ScopeInfoCollection; + private legacyBuilderSet: Set = new Set(); projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); + this.legacyBuilderSet = new Set(); + this.initBuilderMap(); + } + + initBuilderMap(): void { + const moduleList = this.projectConfig?.dependentModuleList; + if (moduleList === undefined) { + return; + } + for (const module of moduleList) { + const language = module.language; + const moduleName = module.moduleName; + if (language !== InteroperAbilityNames.ARKTS_1_1) { + continue; + } + if (!this.legacyBuilderSet.has(moduleName)) { + this.legacyBuilderSet.add(moduleName); + } + } } reset(): void { super.reset(); - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + MonitorCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); + LogCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.push({ name: node.definition!.ident!.name }); + if (arkts.isClassDeclaration(node) && !!node.definition && node.definition.body.length > 0) { + const customComponentInfo = collectCustomComponentScopeInfo(node); + if (!!customComponentInfo) { + this.scope.customComponents.push(customComponentInfo); + } } - if (arkts.isMethodDefinition(node) && this.scopeInfoCollection.customComponents.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfoCollection.customComponents.pop()!; + const scopeInfo = this.scope.customComponents.pop()!; scopeInfo.hasInitializeStruct ||= name === CustomComponentNames.COMPONENT_INITIALIZE_STRUCT; scopeInfo.hasUpdateStruct ||= name === CustomComponentNames.COMPONENT_UPDATE_STRUCT; - scopeInfo.hasReusableRebind ||= name === CustomComponentNames.REUSABLE_COMPONENT_REBIND_STATE; - this.scopeInfoCollection.customComponents.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.pop(); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + this.scope.customComponents.pop(); + } + } + + isFromBuilder1_1(decl: arkts.AstNode | undefined): boolean { + if (!decl || this.legacyBuilderSet.size === 0) { + return false; + } + const moduleName = arkts.getProgramFromAstNode(decl).moduleName?.split('/')[0]; + + if (!this.legacyBuilderSet.has(moduleName)) { + return false; } + + let isFrom1_1 = false; + if (arkts.isMethodDefinition(decl)) { + const annotations = decl.scriptFunction.annotations; + const decorators: string[] = annotations.map((annotation) => { + return (annotation.expr as arkts.Identifier).name; + }); + decorators.forEach((element) => { + if (element === 'Builder') { + isFrom1_1 = true; + return; + } + }); + } + return isFrom1_1; + } + + addcompatibleComponentImport(): void { + ImportCollector.getInstance().collectSource('compatibleComponent', 'arkui.component.interop'); + ImportCollector.getInstance().collectImport('compatibleComponent'); } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); - if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren, this.projectConfig); - return this.visitEachChild(lambda); + if (arkts.isCallExpression(beforeChildren)) { + const decl = arkts.getDecl(beforeChildren.expression); + if (arkts.isIdentifier(beforeChildren.expression) && this.isFromBuilder1_1(decl)) { + // Builder + this.addcompatibleComponentImport(); + return generateBuilderCompatible(beforeChildren, beforeChildren.expression.name); + } else if (isBuilderLambda(beforeChildren, decl)) { + const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); + return this.visitEachChild(lambda); + } } else if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl(beforeChildren); + const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl( + beforeChildren, + this.externalSourceName + ); return this.visitEachChild(lambda); } - const node = this.visitEachChild(beforeChildren); - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - let scope: CustomComponentScopeInfo | undefined; - const scopeInfos: CustomComponentScopeInfo[] = this.scopeInfoCollection.customComponents; - if (scopeInfos.length > 0) { - scope = scopeInfos[scopeInfos.length - 1]; + let node = this.visitEachChild(beforeChildren); + findAndCollectMemoableNode(node, (currNode: arkts.AstNode, nodeType: arkts.Es2pandaAstNodeType) => { + if (builderRewriteByType.has(nodeType)) { + node = builderRewriteByType.get(nodeType)!(currNode); } - const newClass: arkts.ClassDeclaration = tranformClassMembers( - node, - arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), - scope - ); + return currNode; + }); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + const scope: CustomComponentScopeInfo = this.scope.customComponents[this.scope.customComponents.length - 1]; + const newClass: arkts.ClassDeclaration = structFactory.tranformClassMembers(node, scope); this.exit(beforeChildren); return newClass; - } else if (isEntryWrapperClass(node)) { - entryFactory.addMemoToEntryWrapperClassMethods(node); - return node; - } else if (arkts.isClassDeclaration(node) && isEtsGlobalClass(node)) { - return transformEtsGlobalClassMembers(node); - } else if (arkts.isCallExpression(node) && isReourceNode(node)) { - return transformResource(node, this.projectConfig); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); - } else if (arkts.isClassDeclaration(node)) { - return transformObservedTracked(node); - } else if (isArkUICompatible(node)) { - return updateArkUICompatible(node as arkts.CallExpression); - } else if (this.externalSourceName) { - return structFactory.transformExternalSource(this.externalSourceName, node); } - return node; - } -} - -export type ClassScopeInfo = { - isObserved: boolean; - classHasTrack: boolean; - getters: arkts.MethodDefinition[]; -}; - -function transformObservedTracked(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - const isObserved: boolean = hasDecorator(node.definition, DecoratorNames.OBSERVED); - const classHasTrack: boolean = node.definition.body.some( - (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACK) - ); - if (!isObserved && !classHasTrack) { - return node; - } - const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( - node.definition, - node.definition.ident, - node.definition.typeParams, - node.definition.superTypeParams, - [ - ...node.definition.implements, - arkts.TSClassImplements.createTSClassImplements( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('IObservedObject')) - ) - ), - ], - undefined, - node.definition.super, - observedTrackPropertyMembers(classHasTrack, node.definition, isObserved), - node.definition.modifiers, - arkts.classDefinitionFlags(node.definition) - ); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -function observedTrackPropertyMembers( - classHasTrack: boolean, - definition: arkts.ClassDefinition, - isObserved: boolean -): arkts.AstNode[] { - const watchMembers: arkts.AstNode[] = createWatchMembers(); - const permissibleAddRefDepth: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.factory.createNumericLiteral(0), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('int32')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); - - const meta: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('__meta'), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - [arkts.factory.createStringLiteral('@Observe properties (no @Track)')] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - - const getters: arkts.MethodDefinition[] = getGettersFromClassDecl(definition); - - const classScopeInfo: ClassScopeInfo = { - isObserved: isObserved, - classHasTrack: classHasTrack, - getters: getters, - }; - - const propertyTranslators: ObservedTrackTranslator[] = filterDefined( - definition.body.map((it) => classifyObservedTrack(it, classScopeInfo)) - ); - - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - - const nonClassPropertyOrGetter: arkts.AstNode[] = definition.body.filter( - (member) => - !arkts.isClassProperty(member) && - !( - arkts.isMethodDefinition(member) && - arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) - ) - ); - - return [ - ...watchMembers, - ...(classHasTrack ? [permissibleAddRefDepth] : [permissibleAddRefDepth, meta]), - ...collect(...propertyMembers), - ...nonClassPropertyOrGetter, - ...classScopeInfo.getters, - ]; -} - -function createWatchMethod( - methodName: string, - returnType: arkts.Es2pandaPrimitiveType, - paramName: string, - paramType: string, - isReturnStatement: boolean -): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - arkts.factory.createIdentifier(methodName), - arkts.factory.createScriptFunction( - arkts.factory.createBlock([ - isReturnStatement - ? arkts.factory.createReturnStatement( - arkts.factory.createCallExpression(thisSubscribedWatchesMember(methodName), undefined, [ - arkts.factory.createIdentifier(paramName), - ]) - ) - : arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(thisSubscribedWatchesMember(methodName), undefined, [ - arkts.factory.createIdentifier(paramName), - ]) - ), - ]), - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier( - paramName, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(paramType)) - ) - ), - undefined - ), - ], - arkts.factory.createPrimitiveType(returnType), - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); -} - -function createWatchMembers(): arkts.AstNode[] { - const subscribedWatches: arkts.ClassProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier('subscribedWatches'), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('SubscribedWatches')) - ), - [] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('SubscribedWatches')) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - - const addWatchSubscriber = createWatchMethod( - 'addWatchSubscriber', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, - 'watchId', - 'WatchIdType', - false - ); - - const removeWatchSubscriber = createWatchMethod( - 'removeWatchSubscriber', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN, - 'watchId', - 'WatchIdType', - true - ); - - const executeOnSubscribingWatches = createWatchMethod( - 'executeOnSubscribingWatches', - arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, - 'propertyName', - 'string', - false - ); - - return [subscribedWatches, addWatchSubscriber, removeWatchSubscriber, executeOnSubscribingWatches]; -} - -function thisSubscribedWatchesMember(member: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('subscribedWatches'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createIdentifier(member), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); -} - -/** - * @deprecated - */ -function tranformClassMembers( - node: arkts.ClassDeclaration, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - - let classTypeName: string | undefined; - let classOptionsName: string | undefined; - if (isDecl) { - const [classType, classOptions] = getTypeParamsFromClassDecl(node); - classTypeName = getTypeNameFromTypeParameter(classType); - classOptionsName = getTypeNameFromTypeParameter(classOptions); - } - const definition: arkts.ClassDefinition = node.definition; - const className: string | undefined = node.definition.ident?.name; - if (!className) { - throw new Error('Non Empty className expected for Component'); - } - - const propertyTranslators: PropertyTranslator[] = filterDefined( - definition.body.map((it) => classifyProperty(it, className)) - ); - const translatedMembers: arkts.AstNode[] = tranformPropertyMembers( - className, - propertyTranslators, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl, - scope - ); - const updateMembers: arkts.AstNode[] = definition.body - .filter((member) => !arkts.isClassProperty(member)) - .map((member: arkts.AstNode) => - transformOtherMembersInClass(member, classTypeName, classOptionsName, className, isDecl) - ); - - const updateClassDef: arkts.ClassDefinition = structFactory.updateCustomComponentClass(definition, [ - ...translatedMembers, - ...updateMembers, - ]); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -/** - * @deprecated - */ -function transformOtherMembersInClass( - member: arkts.AstNode, - classTypeName: string | undefined, - classOptionsName: string | undefined, - className: string, - isDecl?: boolean -): arkts.AstNode { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - return member; - } - if ( - arkts.isMethodDefinition(member) && - isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && - !isDecl - ) { - return uiFactory.createConstructorMethod(member); + return this.visitorAstNode(node); } - if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { - return structFactory.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); - } - return member; -} -/** - * @deprecated - */ -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.AstNode[] { - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - const collections = []; - if (!scope?.hasInitializeStruct) { - collections.push(structFactory.createInitializeStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (!scope?.hasUpdateStruct) { - collections.push(structFactory.createUpdateStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (currentStructInfo.isReusable) { - collections.push(structFactory.toRecord(optionsTypeName, currentStructInfo.toRecordBody)); - } - return collect(...collections, ...propertyMembers); -} + visitorAstNode(node: arkts.AstNode): arkts.AstNode { + if (isEntryWrapperClass(node)) { + entryFactory.addMemoToEntryWrapperClassMethods(node); + return node; + } else if (arkts.isClassDeclaration(node)) { + return structFactory.transformNormalClass(node, this.externalSourceName); + } else if (arkts.isCallExpression(node)) { + return structFactory.transformCallExpression(node, this.projectConfig, this.resourceInfo); + } else if (arkts.isMethodDefinition(node) && isForEachDecl(node, this.externalSourceName)) { + return structFactory.AddArrowTypeForParameter(node); + } else if (arkts.isTSInterfaceDeclaration(node)) { + return structFactory.tranformInterfaceMembers(node, this.externalSourceName); + } else if ( + arkts.isETSNewClassInstanceExpression(node) && + isSpecificNewClass(node, CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) + ) { + return structFactory.transformCustomDialogController(node); + } -/** - * @deprecated - */ -function transformEtsGlobalClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); + LogCollector.getInstance().emitLogInfo(); + } return node; } - node.definition.body.map((member: arkts.AstNode) => { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - } - return member; - }); - return node; -} - -/** - * @deprecated - */ -function transformResource( - resourceNode: arkts.CallExpression, - projectConfig: ProjectConfig | undefined -): arkts.CallExpression { - const newArgs: arkts.AstNode[] = [ - arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), - arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), - ...resourceNode.arguments, - ]; - return structFactory.generateTransformedResource(resourceNode, newArgs); } diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index 46c7bfbf214585cc1a644848df14634856bf405e..37db0024f5b3ea6ce69facc144e4fa580015e668 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -21,37 +21,47 @@ import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; import { CustomComponentNames, getCustomComponentOptionsName, - createOptionalClassProperty, findLocalImport, + CustomComponentInfo, + collectCustomComponentScopeInfo, + isComponentStruct, + isCustomDialogControllerOptions, + getComponentExtendsName, + ComponentType, } from './utils'; -import { isAnnotation, updateStructMetadata, backingField, expectName, annotation } from '../common/arkts-utils'; -import { EntryWrapperNames, findEntryWithStorageInClassAnnotations } from './entry-translators/utils'; -import { factory as entryFactory } from './entry-translators/factory'; import { - hasDecorator, - DecoratorNames, - getStateManagementType, - collectPropertyDecorators, -} from './property-translators/utils'; + backingField, + expectName, + annotation, + filterDefined, + collect, + createAndInsertImportDeclaration, + isDecoratorAnnotation, +} from '../common/arkts-utils'; +import { ProjectConfig } from '../common/plugin-context'; +import { getEntryParams } from './entry-translators/utils'; +import { factory as entryFactory } from './entry-translators/factory'; +import { hasDecoratorName, findDecoratorInfos } from './property-translators/utils'; import { factory } from './ui-factory'; +import { factory as propertyFactory } from './property-translators/factory'; import { StructMap } from '../common/program-visitor'; -import { generateTempCallFunction } from './interop'; -import { stringify } from 'querystring'; +import { + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + DecoratorIntrinsicNames, + DecoratorNames, + DECORATOR_TYPE_MAP, + ENTRY_POINT_IMPORT_SOURCE_NAME, + NavigationNames, + EntryWrapperNames, +} from '../common/predefines'; +import { generateInstantiateInterop } from './interop/interop'; export interface ComponentTransformerOptions extends VisitorOptions { arkui?: string; + projectConfig?: ProjectConfig; } -type ScopeInfo = { - name: string; - isEntry?: boolean; - isComponent?: boolean; - isReusable?: boolean; -}; - -interface ComponentContext { - structMembers: Map; -} +type ScopeInfo = CustomComponentInfo; export interface InteropContext { className: string; @@ -59,25 +69,37 @@ export interface InteropContext { line?: number; col?: number; arguments?: arkts.ObjectExpression; + content?: arkts.ArrowFunctionExpression; } export class ComponentTransformer extends AbstractVisitor { private scopeInfos: ScopeInfo[] = []; private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = []; private entryNames: string[] = []; - private reusableNames: string[] = []; - private readonly arkui?: string; - private context: ComponentContext = { structMembers: new Map() }; + private structMembersMap: Map = new Map(); private isCustomComponentImported: boolean = false; + private isCustomComponentV2Imported: boolean = false; + private isBaseCustomDialogImported: boolean = false; private isEntryPointImported: boolean = false; - private hasLegacy = false; + private isPageLifeCycleImported: boolean = false; + private isLayoutCallbackImported: boolean = false; + private shouldAddLinkIntrinsic: boolean = false; + private hasLegacy: boolean = false; private legacyStructMap: Map = new Map(); private legacyCallMap: Map = new Map(); + private projectConfig: ProjectConfig | undefined; + private entryRouteName: string | undefined; + private componentType: ComponentType = { + hasComponent: false, + hasComponentV2: false, + hasCustomDialog: false, + hasCustomLayout: false, + }; constructor(options?: ComponentTransformerOptions) { const _options: ComponentTransformerOptions = options ?? {}; super(_options); - this.arkui = _options.arkui; + this.projectConfig = options?.projectConfig; } reset(): void { @@ -85,39 +107,74 @@ export class ComponentTransformer extends AbstractVisitor { this.scopeInfos = []; this.componentInterfaceCollection = []; this.entryNames = []; - this.reusableNames = []; - this.context = { structMembers: new Map() }; + this.structMembersMap = new Map(); this.isCustomComponentImported = false; + this.isCustomComponentV2Imported = false; + this.isBaseCustomDialogImported = false; this.isEntryPointImported = false; + this.isPageLifeCycleImported = false; + this.isLayoutCallbackImported = false; + this.shouldAddLinkIntrinsic = false; this.hasLegacy = false; this.legacyStructMap = new Map(); this.legacyCallMap = new Map(); + this.componentType = { + hasComponent: false, + hasComponentV2: false, + hasCustomDialog: false, + hasCustomLayout: false, + }; } enter(node: arkts.AstNode) { if (arkts.isStructDeclaration(node) && !!node.definition.ident) { - const scopeInfo: ScopeInfo = { name: node.definition.ident.name }; - node.definition.annotations.forEach((anno) => { - scopeInfo.isEntry ||= isAnnotation(anno, CustomComponentNames.ENTRY_ANNOTATION_NAME); - scopeInfo.isComponent ||= isAnnotation(anno, CustomComponentNames.COMPONENT_ANNOTATION_NAME); - scopeInfo.isReusable ||= isAnnotation(anno, CustomComponentNames.RESUABLE_ANNOTATION_NAME); - }); - this.scopeInfos.push(scopeInfo); + const info: ScopeInfo | undefined = collectCustomComponentScopeInfo(node); + if (info) { + this.scopeInfos.push(info); + } } if (arkts.isETSImportDeclaration(node) && !this.isCustomComponentImported) { this.isCustomComponentImported = !!findLocalImport( node, - CustomComponentNames.COMPONENT_DEFAULT_IMPORT, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, 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.isBaseCustomDialogImported) { + this.isBaseCustomDialogImported = !!findLocalImport( + node, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); + } if (arkts.isETSImportDeclaration(node) && !this.isEntryPointImported) { this.isEntryPointImported = !!findLocalImport( node, - EntryWrapperNames.ENTRY_DEFAULT_IMPORT, + ENTRY_POINT_IMPORT_SOURCE_NAME, 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) { @@ -129,22 +186,14 @@ export class ComponentTransformer extends AbstractVisitor { } } - isComponentStruct(): boolean { - if (this.scopeInfos.length === 0) return false; - const scopeInfo: ScopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; - return !!scopeInfo.isComponent; - } - - createImportDeclaration(): void { - const source: arkts.StringLiteral = arkts.factory.create1StringLiteral( - this.arkui ?? CustomComponentNames.COMPONENT_DEFAULT_IMPORT - ); - 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'); } - factory.createAndInsertImportDeclaration( + createAndInsertImportDeclaration( source, imported, imported, @@ -154,20 +203,35 @@ export class ComponentTransformer extends AbstractVisitor { } processEtsScript(node: arkts.EtsScript): arkts.EtsScript { + if (this.isExternal && this.externalSourceName === ENTRY_POINT_IMPORT_SOURCE_NAME) { + const navInterface = entryFactory.createNavInterface(); + navInterface.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + return arkts.factory.updateEtsScript(node, [...node.statements, navInterface]); + } if (this.isExternal && this.componentInterfaceCollection.length === 0 && this.entryNames.length === 0) { return node; } - let updateStatements: arkts.AstNode[] = []; + const updateStatements: arkts.AstNode[] = []; + if (this.shouldAddLinkIntrinsic) { + const expr = arkts.factory.createIdentifier(DecoratorIntrinsicNames.LINK); + updateStatements.push(factory.createIntrinsicAnnotationDeclaration({ expr })); + } if (this.componentInterfaceCollection.length > 0) { - if (!this.isCustomComponentImported) this.createImportDeclaration(); + this.insertComponentImport(); updateStatements.push(...this.componentInterfaceCollection); } if (this.entryNames.length > 0) { if (!this.isEntryPointImported) entryFactory.createAndInsertEntryPointImport(this.program); - // TODO: normally, we should only have at most one @Entry component in a single file. + // 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)); + updateStatements.push( + entryFactory.callRegisterNamedRouter(this.entryRouteName, this.projectConfig, this.program?.absName) + ); + this.createImportDeclaration(ENTRY_POINT_IMPORT_SOURCE_NAME, NavigationNames.NAVINTERFACE); } if (updateStatements.length > 0) { return arkts.factory.updateEtsScript(node, [...node.statements, ...updateStatements]); @@ -175,38 +239,83 @@ export class ComponentTransformer extends AbstractVisitor { return node; } + insertComponentImport(): void { + if (!this.isCustomComponentImported && this.componentType.hasComponent) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_CLASS_NAME + ); + } + if (!this.isCustomComponentV2Imported && this.componentType.hasComponentV2) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + } + if (!this.isBaseCustomDialogImported && this.componentType.hasCustomDialog) { + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); + } + if (!this.isLayoutCallbackImported && this.componentType.hasCustomLayout) { + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.LAYOUT_CALLBACK); + } + } + + 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(), ...node.definition.body], + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); + } + createStaticMethod(definition: arkts.ClassDefinition): arkts.MethodDefinition { + const isDecl: boolean = arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const modifiers = + arkts.classDefinitionFlags(definition) | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC; + const body = isDecl ? undefined : arkts.factory.createBlock([arkts.factory.createReturnStatement()]); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( CustomComponentNames.OPTIONS, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(getCustomComponentOptionsName(definition.ident!.name)) - ) - ) + factory.createTypeReferenceFromString(getCustomComponentOptionsName(definition.ident!.name)) ), undefined ); - - const script = arkts.factory.createScriptFunction( - arkts.factory.createBlock([arkts.factory.createReturnStatement()]), - arkts.FunctionSignature.createFunctionSignature( - undefined, - [param], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - arkts.factory.createIdentifier(CustomComponentNames.BUILDCOMPATIBLENODE), - script, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, - false - ); + const returnTypeAnnotation = arkts.factory.createPrimitiveType(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 factory.createMethodDefinition({ + key, + kind, + function: { + key, + body, + params: [param], + returnTypeAnnotation, + flags, + modifiers, + }, + modifiers, + }); } processComponent( @@ -217,29 +326,23 @@ export class ComponentTransformer extends AbstractVisitor { if (!className || scopeInfo?.name !== className) { return node; } - - arkts.GlobalInfo.getInfoInstance().add(className); - if (arkts.isStructDeclaration(node)) { this.collectComponentMembers(node, className); } - - if (scopeInfo.isReusable) { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - currentStructInfo.isReusable = true; - arkts.GlobalInfo.getInfoInstance().setStructInfo(className, currentStructInfo); - } - - this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers)); - + 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.isEntry) { + if (!!scopeInfo.annotations?.entry) { this.entryNames.push(className); - const entryWithStorage: arkts.ClassProperty | undefined = - findEntryWithStorageInClassAnnotations(definition); - if (!!entryWithStorage) { - newDefinitionBody.push(entryFactory.createEntryLocalStorageInClass(entryWithStorage)); + const { storage, useSharedStorage, routeName } = getEntryParams(definition); + entryFactory.transformStorageParams(storage, useSharedStorage, definition); + if (routeName && routeName.value && arkts.isStringLiteral(routeName.value)) { + this.entryRouteName = routeName.value.str; } } const newDefinition: arkts.ClassDefinition = this.createNewDefinition( @@ -250,8 +353,11 @@ export class ComponentTransformer extends AbstractVisitor { ); if (arkts.isStructDeclaration(node)) { + arkts.classDefinitionSetFromStructModifier(newDefinition); const _node = arkts.factory.createClassDeclaration(newDefinition); _node.modifiers = node.modifiers; + _node.startPosition = node.startPosition; + _node.endPosition = node.endPosition; return _node; } else { return arkts.factory.updateClassDeclaration(node, newDefinition); @@ -274,95 +380,104 @@ export class ComponentTransformer extends AbstractVisitor { staticMethodBody.push(buildCompatibleNode); } } - return arkts.factory.updateClassDefinition( - definition, - definition.ident, - undefined, - undefined, // superTypeParams doen't work - definition.implements, - undefined, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CLASS_NAME), - 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}` - ) - ) - ), - ]) - ) - ), - [ - ...newDefinitionBody, - ...definition.body.map((st: arkts.AstNode) => factory.PreprocessClassPropertyModifier(st)), - ...staticMethodBody, - ], - definition.modifiers, - arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL - ); + const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; + const extendsName: string = getComponentExtendsName(scopeInfo.annotations, this.componentType); + return arkts.factory + .createClassDefinition( + definition.ident, + undefined, + undefined, // superTypeParams doen't work + [...definition.implements, ...factory.generateImplementsForStruct(scopeInfo.annotations)], + undefined, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(extendsName), + arkts.factory.createTSTypeParameterInstantiation([ + factory.createTypeReferenceFromString(className), + factory.createTypeReferenceFromString( + `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}` + ), + ]) + ) + ), + [ + ...newDefinitionBody, + ...definition.body.map((st: arkts.AstNode) => + factory.PreprocessClassPropertyModifier(st, scopeInfo.isDecl) + ), + ...staticMethodBody, + ], + definition.modifiers, + arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL + ) + .setAnnotations(definition.annotations); } - generateComponentInterface(name: string, modifiers: number): arkts.TSInterfaceDeclaration { - const interfaceNode = arkts.factory.createInterfaceDeclaration( - [], - arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), - nullptr, // TODO: wtf - arkts.factory.createInterfaceBody([...(this.context.structMembers.get(name) || [])]), - false, - false - ); + generateComponentInterface( + name: string, + modifiers: number, + annotations?: readonly arkts.AnnotationUsage[] + ): arkts.TSInterfaceDeclaration { + const interfaceNode = arkts.factory + .createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), + nullptr, + arkts.factory.createInterfaceBody([...(this.structMembersMap.get(name) || [])]), + false, + false + ) + .setAnnotations(annotations ?? []); interfaceNode.modifiers = modifiers; return interfaceNode; } collectComponentMembers(node: arkts.StructDeclaration, className: string): void { - const structInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - if (!this.context.structMembers.has(className)) { - this.context.structMembers.set(className, []); - } - node.definition.body.map((it) => { - if (arkts.isClassProperty(it)) { - if (hasDecorator(it, DecoratorNames.PROVIDE)) { - factory.processNoAliasProvideVariable(it); - } - this.context.structMembers.get(className)!.push(...this.createInterfaceInnerMember(it, structInfo)); - } - }); - arkts.GlobalInfo.getInfoInstance().setStructInfo(className, structInfo); + const members = filterDefined( + collect( + ...node.definition.body.filter(arkts.isClassProperty).map((it) => { + if (hasDecoratorName(it, DecoratorNames.PROVIDE)) { + factory.processNoAliasProvideVariable(it); + } + return this.createInterfaceInnerMember(it); + }) + ) + ); + this.structMembersMap.set(className, members); } - createInterfaceInnerMember(member: arkts.ClassProperty, structInfo: arkts.StructInfo): arkts.ClassProperty[] { + createInterfaceInnerMember(member: arkts.ClassProperty): arkts.ClassProperty[] { const originalName: string = expectName(member.key); - const newName: string = backingField(originalName); - - const properties = collectPropertyDecorators(member); - const hasStateManagementType = properties.length > 0; - updateStructMetadata(structInfo, originalName, properties, member.modifiers, hasStateManagementType); - - const originMember: arkts.ClassProperty = createOptionalClassProperty( + const originMember: arkts.ClassProperty = propertyFactory.createOptionalClassProperty( originalName, member, - '', + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - if (member.annotations.length > 0 && !hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - const newMember: arkts.ClassProperty = createOptionalClassProperty( + const infos = findDecoratorInfos(member); + const buildParamInfo = infos.find((it) => + isDecoratorAnnotation(it.annotation, DecoratorNames.BUILDER_PARAM, true) + ); + if (!!buildParamInfo) { + originMember.setAnnotations([buildParamInfo.annotation.clone()]); + return [originMember]; + } + const targetInfo = infos.find((it) => DECORATOR_TYPE_MAP.has(it.name)); + if (!!targetInfo) { + const newName: string = backingField(originalName); + const newMember: arkts.ClassProperty = propertyFactory.createOptionalClassProperty( newName, member, - getStateManagementType(member), + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); + newMember.setAnnotations(member.annotations); + if (isDecoratorAnnotation(targetInfo.annotation, DecoratorNames.LINK, true)) { + this.shouldAddLinkIntrinsic = true; + originMember.setAnnotations([annotation(DecoratorIntrinsicNames.LINK)]); + } return [originMember, newMember]; } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM) && !!originMember.typeAnnotation) { - originMember.typeAnnotation.setAnnotations([annotation('memo')]); - } return [originMember]; } @@ -371,16 +486,16 @@ export class ComponentTransformer extends AbstractVisitor { this.hasLegacy = true; } - processImport(node: arkts.ETSImportDeclaration): void { + processInteropImport(node: arkts.ETSImportDeclaration): void { const source = node.source?.str!; - const specifiers = node.specifiers; + const specifiers = node.specifiers as arkts.ImportSpecifier[]; if (this.legacyStructMap.has(source)) { const structMap = this.legacyStructMap.get(source); if (!structMap) { return; } for (const specifier of specifiers) { - const name = specifier.local.name; + const name = (specifier as arkts.ImportSpecifier)!.local!.name; if (structMap[name]) { this.legacyCallMap.set(name, structMap[name]); } @@ -388,42 +503,71 @@ export class ComponentTransformer extends AbstractVisitor { } } + processInteropCall(node: arkts.CallExpression): arkts.CallExpression { + const ident = node.expression; + if (!(ident instanceof arkts.Identifier)) { + return node; + } + const className = ident.name; + const trailingBlock = node.trailingBlock; + const content = trailingBlock ? arkts.factory.createArrowFunction( + factory.createScriptFunction({ + body: trailingBlock, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ) : undefined; + if (this.legacyCallMap.has(className)) { + const path = this.legacyCallMap.get(className)!; + const args = node.arguments; + const context: InteropContext = { + className: className, + path: path, + arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression ? args[0] : undefined, + content: content + }; + return generateInstantiateInterop(context); + } + return node; + } + visitor(node: arkts.AstNode): arkts.AstNode { this.enter(node); const newNode = this.visitEachChild(node); if (arkts.isEtsScript(newNode)) { return this.processEtsScript(newNode); } - if (arkts.isStructDeclaration(newNode) && this.isComponentStruct()) { + if ( + arkts.isStructDeclaration(newNode) && + this.scopeInfos.length > 0 && + isComponentStruct(newNode, this.scopeInfos[this.scopeInfos.length - 1]) + ) { const updateNode = this.processComponent(newNode); this.exit(newNode); return updateNode; } + if ( + arkts.isClassDeclaration(newNode) && + this.externalSourceName === ENTRY_POINT_IMPORT_SOURCE_NAME && + newNode.definition?.ident?.name === EntryWrapperNames.ENTRY_POINT_CLASS_NAME + ) { + return this.updateEntryPoint(newNode); + } + if ( + arkts.isTSInterfaceDeclaration(newNode) && + isCustomDialogControllerOptions(newNode, this.externalSourceName) + ) { + return factory.updateCustomDialogOptionsInterface(newNode); + } + // process interop code if (!this.hasLegacy) { return newNode; } if (arkts.isETSImportDeclaration(newNode)) { - this.processImport(newNode); + this.processInteropImport(newNode); } if (arkts.isCallExpression(newNode)) { - const ident = newNode.expression; - if (!(ident instanceof arkts.Identifier)) { - return newNode; - } - const className = ident.name; - if (this.legacyCallMap.has(className)) { - const path = this.legacyCallMap.get(className)!; - // const pathName = 'path/har1'; - const args = newNode.arguments; - const context: InteropContext = { - className: className, - path: path, - arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression - ? args[0] - : undefined - }; - return generateTempCallFunction(context); - } + return this.processInteropCall(newNode); } return newNode; } diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index fca4fe867a8113195baff5e824865191ba73b7d9..e4dd29d224a73bb929913285ac9d5fb2cb602a91 100644 --- a/arkui-plugins/ui-plugins/entry-translators/factory.ts +++ b/arkui-plugins/ui-plugins/entry-translators/factory.ts @@ -14,12 +14,13 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getInteropPath } from '../../path'; -const interop = require(getInteropPath()); -const nullptr = interop.nullptr; -import { EntryWrapperNames } from './utils'; -import { annotation } from '../../common/arkts-utils'; +import * as path from 'path'; +import { annotation, createAndInsertImportDeclaration } from '../../common/arkts-utils'; +import { ENTRY_POINT_IMPORT_SOURCE_NAME, EntryWrapperNames, NavigationNames } from '../../common/predefines'; +import { ProjectConfig } from '../../common/plugin-context'; import { factory as uiFactory } from '../ui-factory'; +import { getRelativePagePath } from './utils'; +import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; export class factory { /** @@ -83,11 +84,7 @@ export class factory { */ static generateEntryFunction(name: string): arkts.MethodDefinition { const exp = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier(name), - undefined, - [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later - ) + arkts.factory.createCallExpression(arkts.factory.createIdentifier(name), undefined, []) ); const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); const block = arkts.factory.createBlock([exp]); @@ -148,11 +145,7 @@ export class factory { */ static generateEntryProperty(name: string): arkts.ClassProperty { const exp = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier(name), - undefined, - [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later - ) + arkts.factory.createCallExpression(arkts.factory.createIdentifier(name), undefined, []) ); const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); const block: arkts.BlockStatement = arkts.factory.createBlock([exp]); @@ -226,7 +219,8 @@ export class factory { !!member.scriptFunction.id && member.scriptFunction.id.name === EntryWrapperNames.ENTRY_FUNC ) { - member.scriptFunction.setAnnotations([annotation('memo')]); + addMemoAnnotation(member.scriptFunction); + arkts.NodeCache.getInstance().collect(member); } }); } @@ -274,13 +268,13 @@ export class factory { * to the top of script's statements. */ static createAndInsertEntryPointImport(program?: arkts.Program) { - const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(EntryWrapperNames.ENTRY_DEFAULT_IMPORT); + const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(ENTRY_POINT_IMPORT_SOURCE_NAME); const imported: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME); // Insert this import at the top of the script's statements. if (!program) { throw Error('Failed to insert import: Transformer has no program'); } - uiFactory.createAndInsertImportDeclaration( + createAndInsertImportDeclaration( source, imported, imported, @@ -288,4 +282,186 @@ export class factory { program ); } + + /** + * transform `@Entry` storage params, e.g. `@Entry`({useSharedStorage: ..., storage: ...}) + */ + static transformStorageParams( + storage: arkts.ClassProperty | undefined, + useSharedStorage: arkts.ClassProperty | undefined, + definition: arkts.ClassDefinition + ): void { + if (!storage && !useSharedStorage) { + return; + } + const ctor: arkts.MethodDefinition | undefined = definition.body.find( + (member) => + arkts.isMethodDefinition(member) && + member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR + ) as arkts.MethodDefinition | undefined; + if (!ctor) { + return; + } + let sharedArg = arkts.factory.createBooleanLiteral(false); + if (useSharedStorage && useSharedStorage.value && arkts.isBooleanLiteral(useSharedStorage.value)) { + sharedArg = useSharedStorage.value; + } + let storageArg = arkts.factory.createUndefinedLiteral(); + if (storage && storage.value && arkts.isStringLiteral(storage.value)) { + storageArg = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(storage.value.str), + undefined, + undefined + ); + } + const superCall = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(arkts.factory.createSuperExpression(), undefined, [ + sharedArg, + storageArg, + ]) + ); + if (ctor.scriptFunction.body && arkts.isBlockStatement(ctor.scriptFunction.body)) { + ctor.scriptFunction.setBody( + arkts.factory.updateBlock(ctor.scriptFunction.body, [...ctor.scriptFunction.body.statements, superCall]) + ); + } + } + + /** + * helper for callRegisterNamedRouter to generate NavInterface arg + */ + static navInterfaceArg( + projectConfig: ProjectConfig | undefined, + fileAbsName: string | undefined + ): arkts.TSAsExpression { + const projectRoot = projectConfig?.moduleRootPath + ? path.join(projectConfig.moduleRootPath, 'src', 'main', 'ets') + : ''; + const pageFullPath = getRelativePagePath(projectConfig?.projectPath ?? '', fileAbsName ?? ''); + const pagePath = getRelativePagePath(projectRoot, fileAbsName ?? ''); + return arkts.factory.createTSAsExpression( + arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + factory.createNavProperty(NavigationNames.BUNDLE_NAME, projectConfig?.bundleName), + factory.createNavProperty(NavigationNames.MODULE_NAME, projectConfig?.moduleName), + factory.createNavProperty(NavigationNames.PAGE_PATH, pagePath), + factory.createNavProperty(NavigationNames.PAGE_FULL_PATH, pageFullPath), + factory.createNavProperty(NavigationNames.INTEGRATED_HSP, projectConfig?.integratedHsp?.toString()), + ], + false + ), + uiFactory.createTypeReferenceFromString(NavigationNames.NAVINTERFACE), + false + ); + } + + /** + * helper for navInterfaceArg to generate class properties, e.g. buneleName: '...' + */ + static createNavProperty(key: NavigationNames, value: string | undefined): arkts.Property { + return arkts.factory.createProperty( + arkts.factory.createIdentifier(key), + arkts.factory.createStringLiteral(value ?? '') + ); + } + + /** + * generate __EntryWrapper.RegisterNamedRouter(...) + */ + static callRegisterNamedRouter( + entryRouteName: string | undefined, + projectConfig: ProjectConfig | undefined, + fileAbsName: string | undefined + ): arkts.ExpressionStatement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME), + arkts.factory.createIdentifier(EntryWrapperNames.REGISTER_NAMED_ROUTER), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral(entryRouteName ?? ''), + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME) + ) + ), + [] + ), + factory.navInterfaceArg(projectConfig, fileAbsName), + ] + ) + ); + } + + /** + * generate interface NavInterface in header arkui.UserView + */ + static createNavInterface(): arkts.TSInterfaceDeclaration { + return arkts.factory.createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(NavigationNames.NAVINTERFACE), + undefined, + arkts.factory.createInterfaceBody([ + this.createClassProp(NavigationNames.BUNDLE_NAME), + this.createClassProp(NavigationNames.MODULE_NAME), + this.createClassProp(NavigationNames.PAGE_PATH), + this.createClassProp(NavigationNames.PAGE_FULL_PATH), + this.createClassProp(NavigationNames.INTEGRATED_HSP), + ]), + false, + false + ); + } + + /** + * helper for createNavInterface to generate class properties + */ + static createClassProp(propName: string): arkts.ClassProperty { + return arkts.factory.createClassProperty( + arkts.factory.createIdentifier(propName), + undefined, + uiFactory.createTypeReferenceFromString('string'), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /** + * helper for generateRegisterNamedRouter to generate param decl, e.g: `routerName: string` + */ + static registerRouteParam(name: EntryWrapperNames, type: string): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(name, uiFactory.createTypeReferenceFromString(type)), + undefined + ); + } + + /** + * generate generateRegisterNamedRouter method in header arkui.UserView + */ + static generateRegisterNamedRouter(): arkts.MethodDefinition { + const params = [ + factory.registerRouteParam(EntryWrapperNames.ROUTER_NAME, 'string'), + factory.registerRouteParam(EntryWrapperNames.INSTANCE, EntryWrapperNames.ENTRY_POINT_CLASS_NAME), + factory.registerRouteParam(EntryWrapperNames.PARAM, NavigationNames.NAVINTERFACE), + ]; + return uiFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(EntryWrapperNames.REGISTER_NAMED_ROUTER), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock([]), + params: params, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + }); + } } diff --git a/arkui-plugins/ui-plugins/entry-translators/utils.ts b/arkui-plugins/ui-plugins/entry-translators/utils.ts index 61a03783c1351832c2f132559c3e393c8257a936..5783f94ca15adc497d89193fc661530bfbed2a6e 100644 --- a/arkui-plugins/ui-plugins/entry-translators/utils.ts +++ b/arkui-plugins/ui-plugins/entry-translators/utils.ts @@ -14,18 +14,10 @@ */ import * as arkts from '@koalaui/libarkts'; +import * as path from 'path'; import { factory } from './factory'; import { isAnnotation } from '../../common/arkts-utils'; -import { CustomComponentNames } from '../utils'; - -export enum EntryWrapperNames { - ENTRY_FUNC = 'entry', - WRAPPER_CLASS_NAME = '__EntryWrapper', - ENTRY_STORAGE_ANNOTATION_KEY = 'storage', - ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME = '_entry_local_storage_', - ENTRY_DEFAULT_IMPORT = 'arkui.UserView', - ENTRY_POINT_CLASS_NAME = 'EntryPoint', -} +import { StructDecoratorNames, EntryParamNames, EntryWrapperNames } from '../../common/predefines'; /** * @deprecated @@ -67,18 +59,37 @@ export function isEntryWrapperClass(node: arkts.AstNode): node is arkts.ClassDec } /** - * find `{storage: ""}` in `@Entry({storage: ""})` (i.e. annotation's properties). + * get annotation's properties in `@Entry()`: storage, useSharedStorage, routeName. * * @param node class definition node */ -export function findEntryWithStorageInClassAnnotations(node: arkts.ClassDefinition): arkts.ClassProperty | undefined { - const annotation = node.annotations.find((anno) => { - if (!isAnnotation(anno, CustomComponentNames.ENTRY_ANNOTATION_NAME)) return false; - const property = anno.properties?.at(0); - if (!property || !arkts.isClassProperty(property)) return false; - if (!property.key || !arkts.isIdentifier(property.key)) return false; - if (!property.value || !arkts.isStringLiteral(property.value)) return false; - return property.key.name === EntryWrapperNames.ENTRY_STORAGE_ANNOTATION_KEY; - }); - return annotation?.properties?.at(0) as arkts.ClassProperty | undefined; -} +export function getEntryParams(node: arkts.ClassDefinition): Record { + const annotation = node.annotations.find((anno) => isAnnotation(anno, StructDecoratorNames.ENTRY)); + const result = { + storage: undefined, + useSharedStorage: undefined, + routeName: undefined, + } as Record; + if (!annotation || !annotation.properties) { + return result; + } + for (const prop of annotation.properties) { + if (arkts.isClassProperty(prop) && prop.key && arkts.isIdentifier(prop.key)) { + const name = prop.key.name as EntryParamNames; + if (name in result) { + result[name] = prop; + } + } + } + return result; +} + +/** + * Computes and formats a relative path by removing `.ets` extension and normalizing path separators to `/`. + */ +export function getRelativePagePath(from: string, to: string): string { + return path + .relative(from, to) + .replace(/\\/g, '/') + .replace(/\.ets$/, ''); +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/index.ts b/arkui-plugins/ui-plugins/index.ts index 1397e89c23400d38951a60aa9761b1f8352bd312..1785e4854352aa9d54977fe12a7ed1909928947e 100644 --- a/arkui-plugins/ui-plugins/index.ts +++ b/arkui-plugins/ui-plugins/index.ts @@ -15,7 +15,6 @@ import * as arkts from '@koalaui/libarkts'; import { ComponentTransformer } from './component-transformer'; -import { PreprocessorTransformer } from './preprocessor-transform'; import { CheckedTransformer } from './checked-transformer'; import { Plugins, PluginContext, ProjectConfig } from '../common/plugin-context'; import { ProgramVisitor } from '../common/program-visitor'; @@ -36,21 +35,25 @@ export function uiTransform(): Plugins { function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; console.log('[UI PLUGIN] AFTER PARSED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('ArkTS:Parse'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:AfterParse'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; const cachePath: string | undefined = this.getProjectConfig()?.cachePath; + const canSkipPhases = program.canSkipPhases(); debugLog('[BEFORE PARSED SCRIPT] script: ', script.dumpSrc()); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 1, 'UI_AfterParse_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('ui-parsed'); - program = parsedProgramVisit(program, this); + program = parsedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-parsed', true); debugLog('[AFTER PARSED SCRIPT] script: ', script.dumpSrc()); @@ -59,9 +62,12 @@ function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 2, 'UI_AfterParse_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:AfterParse'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:AfterParse'); console.log('[UI PLUGIN] AFTER PARSED EXIT'); return script; } @@ -78,12 +84,13 @@ function parsedProgramVisit( debugLog('[SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); } else { debugLog('[CANT SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); - const componentTransformer = new ComponentTransformer(); - const preprocessorTransformer = new PreprocessorTransformer(); + const componentTransformer = new ComponentTransformer({ + projectConfig: context.getProjectConfig(), + }); const programVisitor = new ProgramVisitor({ pluginName: uiTransform.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, - visitors: [componentTransformer, preprocessorTransformer], + visitors: [componentTransformer], skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, pluginContext: context, }); @@ -95,21 +102,26 @@ function parsedProgramVisit( function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { let script: arkts.EtsScript | undefined; console.log('[UI PLUGIN] AFTER CHECKED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + arkts.Performance.getInstance().memoryTrackerPrintCurrent('ArkTS:Check'); + arkts.Performance.getInstance().memoryTrackerGetDelta('ArkTS:Check'); + arkts.Performance.getInstance().memoryTrackerReset(); + arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:UI-AfterCheck'); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; script = program.astNode; const cachePath: string | undefined = this.getProjectConfig()?.cachePath; + const canSkipPhases = program.canSkipPhases(); debugLog('[BEFORE STRUCT SCRIPT] script: ', script.dumpSrc()); debugDump( script.dumpSrc(), getDumpFileName(0, 'SRC', 3, 'UI_AfterCheck_Begin'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); arkts.Performance.getInstance().createEvent('ui-checked'); - program = checkedProgramVisit(program, this); + program = checkedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-checked', true); debugLog('[AFTER STRUCT SCRIPT] script: ', script.dumpSrc()); @@ -118,14 +130,11 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { getDumpFileName(0, 'SRC', 4, 'UI_AfterCheck_End'), true, cachePath, - program.programFileNameWithExtension + program.fileNameWithExtension ); - arkts.GlobalInfo.getInfoInstance().reset(); - arkts.Performance.getInstance().createEvent('ui-recheck'); - arkts.recheckSubtree(script); - arkts.Performance.getInstance().stopEvent('ui-recheck', true); - arkts.Performance.getInstance().clearAllEvents(); this.setArkTSAst(script); + arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:UI-AfterCheck'); + arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:UI-AfterCheck'); console.log('[UI PLUGIN] AFTER CHECKED EXIT'); return script; } @@ -143,6 +152,9 @@ function checkedProgramVisit( } else { debugLog('[CANT SKIP PHASE] phase: ui-checked, moduleName: ', program.moduleName); const projectConfig: ProjectConfig | undefined = context.getProjectConfig(); + if (projectConfig && !projectConfig.appResource) { + projectConfig.ignoreError = true; + } const checkedTransformer = new CheckedTransformer(projectConfig); const programVisitor = new ProgramVisitor({ pluginName: uiTransform.name, @@ -154,4 +166,4 @@ function checkedProgramVisit( program = programVisitor.programVisitor(program); } return program; -} \ No newline at end of file +} diff --git a/arkui-plugins/ui-plugins/initstatevar.ts b/arkui-plugins/ui-plugins/initstatevar.ts deleted file mode 100644 index 3264db1a243506492937198b261acd3bf95b1d9b..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-plugins/initstatevar.ts +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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 { InteroperAbilityNames } from '../common/predefines'; -import { annotation, backingField, isAnnotation } from '../common/arkts-utils'; -import { getPropertyESValue, getWrapValue, setPropertyESValue } from './interop'; - - -export function processNormal(keyName: string, value: arkts.AstNode): arkts.Statement[] { - const result: arkts.Statement[] = []; - const setProperty = setPropertyESValue( - InteroperAbilityNames.PARAM, - keyName, - getWrapValue(value) - ); - result.push(setProperty); - return result; -} - -export function createVariableLet(varName: string, expression: arkts.AstNode): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(varName), - expression - )] - ); -} -export function setValueCallback(name: string, type: arkts.TypeNode, block: arkts.BlockStatement): arkts.AstNode { - return createVariableLet(name, - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - block, - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('value', type), - undefined, - ), - ], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ); -} - -function createProxyBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(varName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('value') - ) - ) - ] - ); -} - -export function setCallbackForProxy(varName: string, type: arkts.TypeNode): arkts.Statement[] { - const createCallback = setValueCallback(addStatePrefix(varName, 'SetSource'), type, createProxyBlock(varName)); - const createProxyState = createVariableLet(addStatePrefix(varName, 'ProxyState'), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('createState'), - arkts.factory.createIdentifier('invoke'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - getWrapValue( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(varName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - getWrapValue(arkts.factory.createIdentifier(addStatePrefix(varName, 'SetSource'))) - ] - ) - ); - const setProxy = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setProxy'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState'))], - ) - ); - return [createCallback, createProxyState, setProxy]; -} - -function createSourceBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')), - arkts.factory.createIdentifier('invokeMethod'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral('set'), - getWrapValue( - arkts.factory.createIdentifier('value') - ) - ] - ) - ) - ] - ); -} - -function createNotifyBlock(varName: string): arkts.BlockStatement { - return arkts.factory.createBlock( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')), - arkts.factory.createIdentifier('invokeMethod'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral('notifyPropertyHasChangedPU') - ] - ) - ) - ] - ); -} - -function setNotifyForSource(varName: string): arkts.Statement[] { - const block = createNotifyBlock(varName); - const createCallback = createVariableLet(addStatePrefix(varName, 'NotifyCallback'), - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - block, - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('propertyName', - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('string') - ) - ) - ), - undefined, - ), - ], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ); - const setCallback = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setNotifyCallback'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createIdentifier(addStatePrefix(varName, 'NotifyCallback'))], - ) - ); - return [createCallback, setCallback]; -} - -export function setCallbackForSource(varName: string, type: arkts.TypeNode): arkts.Statement[] { - const createCallback = setValueCallback(addStatePrefix(varName, 'SetProxy'), type, createSourceBlock(varName)); - const setFunc = arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(backingField(varName)), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ), - arkts.factory.createIdentifier('setProxyValue'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier(addStatePrefix(varName, 'SetProxy')) - ) - ); - const setNotify = setNotifyForSource(varName); - return [createCallback, setFunc, ...setNotify]; -} - -export function processLink(keyName: string, value: arkts.AstNode, type: arkts.TypeNode, proxySet: Set): arkts.Statement[] { - const varName = ((value as arkts.MemberExpression).property as arkts.Identifier).name; - const result: arkts.Statement[] = []; - if (!proxySet.has(varName)) { - proxySet.add(varName); - const setProxy = setCallbackForProxy(varName, type); - result.push(...setProxy); - const setSource = setCallbackForSource(varName, type); - result.push(...setSource); - } - const setParam = setPropertyESValue( - 'param', - keyName, - arkts.factory.createIdentifier(addStatePrefix(varName, 'ProxyState')) - ); - result.push(setParam); - return result; -} - -export function hasLink(decorators: string[]): boolean { - return decorators.some(decorator => decorator === 'Link'); -} - -function addStatePrefix(stateVarName: string, name: string): string { - return `${stateVarName}_${name}`; -} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/builder-interop.ts b/arkui-plugins/ui-plugins/interop/builder-interop.ts new file mode 100644 index 0000000000000000000000000000000000000000..f2073b52d206698b503f71095287983f1f87a4df --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/builder-interop.ts @@ -0,0 +1,538 @@ +/* + * Copyright (c) 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 { ESValueMethodNames, BuilderMethodNames, InteroperAbilityNames, BuilderParams } from './predefines'; +import { + createELMTID, + createEmptyESValue, + createGlobal, + createInitReturn, + getPropertyESValue, + getWrapValue, + setPropertyESValue +} from './utils'; + +interface builderParam { + args: arkts.AstNode[], + paramsInfo: arkts.Statement[] +} + +function invokeFunctionWithParam(functionName: string, result: string, className: string, args: arkts.AstNode[]): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + getWrapValue(arkts.factory.createIdentifier(className)), + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + ...args + ] + ) + )] + ); +} + +function invokeRunPendingJobs(): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderMethodNames.RUNPENDINGJOBS), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); +} + +function invokeComponent(): arkts.Statement[] { + const viewPU = getPropertyESValue('viewPUCreate', InteroperAbilityNames.GLOBAL, 'viewPUCreate'); + const create = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('viewPUCreate'), + arkts.factory.createIdentifier('invoke'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ] + ) + ); + return [viewPU, create]; +} + +function createBuilderInitializer(className: string, functionName: string, param: builderParam): arkts.ArrowFunctionExpression { + const block = arkts.factory.createBlock( + [ + createGlobal(), + ...createELMTID(), + getPropertyESValue(BuilderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.GLOBAL, functionName), + ...param.paramsInfo, + invokeFunctionWithParam(BuilderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.COMPONENT, className, param.args), + ...invokeComponent(), + createInitReturn(className) + ] + ); + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + block, + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +/** + * getInstanceParam + * @returns let instanceParam = (instance.getProperty("arg1") as ESValue); + */ +function getInstanceParam(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createTSAsExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral('arg1') + ] + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ) + ) + ] + ); +} + +/** + * instanceParamTypeOf + * @returns + * if (((instanceParam.typeOf()) != ("object"))) { + return; + } + */ +function instanceParamTypeOf(): arkts.Statement { + return arkts.factory.createIfStatement( + arkts.factory.createBinaryExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createIdentifier('typeOf'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ), arkts.factory.createStringLiteral('object'), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_EQUAL + ), + arkts.factory.createBlock([ + arkts.factory.createReturnStatement() + ]) + ); +} + +/** + * getParamWrapped + * @param argument param + * @returns let param_wrapped = ESValue.wrap(param); + */ +function getParamWrapped(argument: arkts.Identifier): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('ESValue'), + arkts.factory.createIdentifier('wrap'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + argument + ] + ) + ) + ] + ); +} + +/** + * getItem + * @returns let param_wrapped_it = param_wrapped.keys(); + */ +function getItem(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_IT), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createIdentifier('keys'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +function getResult(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_IT), + arkts.factory.createIdentifier('next'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +function getResultDone(): arkts.Statement { + return arkts.factory.createIfStatement( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('done'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createBlock([ + arkts.BreakStatement.createBreakStatement() + ]) + ); +} + +function getParamWrappedProperty(): arkts.Statement { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ), + ] + ); +} + +function setInstanceParam(): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.INSTANCEPARAM), + arkts.factory.createIdentifier(ESValueMethodNames.SETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(BuilderParams.PARAM_WRAPPED_KEY), + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ), + false + ), + getParamWrappedProperty() + ] + ) + ); +} + +/** + * getWhile + * @returns + * while (true) { + let param_wrapped_key = param_wrapped_it.next(); + if (param_wrapped_key.done) { + break; + } + instanceParam.setProperty((param_wrapped_key.value as ESValue), + param_wrapped.getProperty((param_wrapped_key.value as ESValue))); + } + */ +function getWhile(): arkts.Statement { + return arkts.WhileStatement.createWhileStatement( + arkts.factory.createBooleanLiteral(true), + arkts.factory.createBlock( + [ + getResult(), + getResultDone(), + setInstanceParam() + ] + ) + ); +} + +function getUpdateArgs(node: arkts.CallExpression): arkts.Statement[] { + if (node.arguments.length !== 1) { + return []; + } + let body: arkts.Statement[] = []; + let argument = node.arguments[0]; + if (arkts.isObjectExpression(argument)) { + body?.push(getPropertyESValue('param', InteroperAbilityNames.INSTANCE, 'arg1')); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body?.push(setPropertyESValue('param', key.name, getWrapValue(value))); + } + const endBody = + [ + createGlobal(), + getPropertyESValue(BuilderMethodNames.RUNPENDINGJOBS, + InteroperAbilityNames.GLOBAL, + BuilderMethodNames.RUNPENDINGJOBS), + invokeRunPendingJobs() + ]; + body?.push(...endBody); + } else if (arkts.isIdentifier(argument)) { + const functionBody = + [ + getInstanceParam(), + instanceParamTypeOf(), + getParamWrapped(argument), + getItem(), + getWhile(), + createGlobal(), + getPropertyESValue(BuilderMethodNames.RUNPENDINGJOBS, + InteroperAbilityNames.GLOBAL, + BuilderMethodNames.RUNPENDINGJOBS), + invokeRunPendingJobs() + ]; + body?.push(...functionBody); + } + + return body; +} + +function createBuilderUpdate(node: arkts.CallExpression): arkts.ArrowFunctionExpression { + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock( + [ + ...getUpdateArgs(node) + ] + ), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ) + ), + undefined, + ), + ], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +function getInitArgs(node: arkts.CallExpression): builderParam { + const args: arkts.AstNode[] = []; + let ObjectExpressionNum: number = 0; + const body: arkts.Statement[] = []; + node.arguments.forEach((argument) => { + if (arkts.isObjectExpression(argument)) { + processArgument(argument, ObjectExpressionNum, body, args); + ObjectExpressionNum++; + } else { + args.push(getWrapValue(argument)); + } + }); + return { args: args, paramsInfo: body }; +} + +function processArgument(argument: arkts.ObjectExpression, ObjectExpressionNum:number, + body: arkts.Statement[], args: arkts.AstNode[]): void { + const paramName: string = 'paramObject' + ObjectExpressionNum; + body.push(createEmptyESValue(paramName)); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body.push(setPropertyESValue(paramName, key.name, getWrapValue(value))); + } + args.push(arkts.factory.createIdentifier(paramName)); +} + +/** + * + * @param node node + * @param moduleName moduleName + * @returns After Checked, transform builder/WrappedBuilder -> compatibleComponent + */ +export function generateBuilderCompatible(node: arkts.CallExpression, moduleName: string): arkts.CallExpression { + let functionName = getFunctionName(node); + let param: builderParam = getInitArgs(node); + const initializer = createBuilderInitializer(moduleName, functionName, param); + const updater: arkts.ArrowFunctionExpression = createBuilderUpdate(node); + const result = arkts.factory.updateCallExpression( + node, + arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), + undefined, + [ + initializer, + updater, + ] + ); + arkts.NodeCache.getInstance().collect(result); + return result; +} + +function getFunctionName(node: arkts.CallExpression): string { + switch (node.arguments.length) { + case 0: + return 'createCompatibleNodeWithFuncVoid'; + case 1: + return 'createCompatibleNodeWithFunc'; + case 2: + return 'createCompatibleNodeWithFunc2'; + case 3: + return 'createCompatibleNodeWithFunc3'; + case 4: + return 'createCompatibleNodeWithFunc4'; + case 5: + return 'createCompatibleNodeWithFunc5'; + case 6: + return 'createCompatibleNodeWithFunc6'; + case 7: + return 'createCompatibleNodeWithFunc7'; + case 8: + return 'createCompatibleNodeWithFunc8'; + case 9: + return 'createCompatibleNodeWithFunc9'; + case 10: + return 'createCompatibleNodeWithFunc10'; + default: + throw Error('Error arguments in Legacy Builder Function'); + } +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/initstatevar.ts b/arkui-plugins/ui-plugins/interop/initstatevar.ts new file mode 100644 index 0000000000000000000000000000000000000000..14225718e759121074bfcb9f97b4ab87748d3c78 --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/initstatevar.ts @@ -0,0 +1,248 @@ +/* + * Copyright (c) 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 { BuilderMethodNames, InteroperAbilityNames } from './predefines'; +import { annotation, backingField, isAnnotation } from '../../common/arkts-utils'; +import { stateProxy, getWrapValue, setPropertyESValue, createEmptyESValue } from './utils'; +import { hasDecorator } from '../property-translators/utils'; +import { DecoratorNames } from '../../common/predefines'; + + +export function initialArgs(args: arkts.ObjectExpression, varMap: Map, updateProp: arkts.Property[]): arkts.Statement[] { + const result: arkts.Statement[] = []; + const proxySet = new Set(); + + + for (const property of args.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value!; + if (!(key instanceof arkts.Identifier)) { + throw Error('Error arguments in Legacy Component'); + } + const keyName = key.name; + const keyProperty = varMap.get(keyName); + if (keyProperty === undefined) { + throw Error('Error arguments in Legacy Component'); + } + const keyType = keyProperty.typeAnnotation!; + const annotations = keyProperty.annotations; + if (annotations.length === 0) { + const valueProperty = arkts.getDecl(value); + if (valueProperty instanceof arkts.ClassProperty && (hasDecorator(valueProperty, DecoratorNames.PROVIDE) || + hasDecorator(valueProperty, DecoratorNames.CONSUME))) { + throw Error('Cannot assign @Provide or @Consume decorated data to regular property.'); + } + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.LINK)) { + const initParam = processLink(keyName, value, keyType, proxySet); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.CONSUME)) { + throw Error('The @Consume property cannot be assigned.'); + } else if (hasDecorator(keyProperty, DecoratorNames.PROP) || hasDecorator(keyProperty, DecoratorNames.OBJECT_LINK)) { + updateProp.push(property); + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.STATE) || hasDecorator(keyProperty, DecoratorNames.PROVIDE)) { + const initParam = processNormal(keyName, value); + result.push(...initParam); + } else if (hasDecorator(keyProperty, DecoratorNames.CONSUME)) { + throw Error('The @Consume property cannot be assigned.'); + } else if (hasDecorator(keyProperty, DecoratorNames.BUILDER_PARAM)) { + const initParam = processBuilderParam(keyName, value); + result.push(...initParam); + } else { + const initParam = processNormal(keyName, value); + result.push(...initParam); + } + } + return result; +} + +export function createVariableLet(varName: string, expression: arkts.AstNode): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(varName), + expression + )] + ); +} + +function createBackingFieldExpression(varName: string): arkts.TSNonNullExpression { + return arkts.factory.createTSNonNullExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(backingField(varName)), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + ); +} + + +function getStateProxy(proxyName: string, stateVar: () => arkts.Expression): arkts.Statement { + return createVariableLet( + proxyName, + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.GETCOMPATIBLESTATE), + undefined, + [ + stateVar() + ] + ) + ); +} + +/** + * Processes a nested object literal and generates code to instantiate and populate it using ESValue methods. + * + * Converts a nested object structure into a sequence of statements that: + * 1. Instantiate empty objects via `ESValue.instantiateEmptyObject()` + * 2. Sets properties on these objects using `setProperty()` + * 3. Nests objects by assigning child objects as properties of parent objects + * + * @param target - A nested object literal (e.g., { b: { c: { d: '1' }, cc: { d: '1' } } }) + * @returns Generated code statements that reconstruct the input object using ESValue APIs + * @example + * Input: { b: { c: { d: '1' }, cc: { d: '1' } } } + * Output: + * let param0 = ESValue.instantiateEmptyObject(); + * let param00 = ESValue.instantiateEmptyObject(); + * param00.setProperty("d", ESValue.wrap("1")); + * param0.setProperty("c", param00); + * let param01 = ESValue.instantiateEmptyObject(); + * param01.setProperty("d", ESValue.wrap("1")); + * param0.setProperty("cc", param01); + * param.setProperty("b", param0); + */ +function processObjectLiteral(target: arkts.ObjectExpression, curParam: string, result: arkts.Statement[], keyName: string): void { + if (curParam !== InteroperAbilityNames.PARAM) { + const createParam = createEmptyESValue(curParam); + result.push(createParam); + } + target.properties.forEach((property: { key: arkts.Expression; value: arkts.Expression; }) => { + const paramName = curParam + keyName; + const key = property.key; + const value = property.value; + if (arkts.isObjectExpression(value)) { + processObjectLiteral(value, paramName, result, keyName); + const setProperty = setPropertyESValue( + curParam, + key.name, + arkts.factory.createIdentifier(paramName) + ); + result.push(setProperty); + } else { + const setProperty = setPropertyESValue( + curParam, + key.name, + getWrapValue(value) + ); + result.push(setProperty); + } + }); +} + + +/** + * + * @param keyName - The name of the state variable (e.g., state) + * @returns generate code to process @Link data interoperability + * @example + * Input: {link: this.state} + * Output: + * let __Proxy_state = getCompatibleState(this.state); + * param.setProperty("link", __Proxy_state); + */ +export function processLink(keyName: string, value: arkts.Expression, type: arkts.TypeNode, proxySet: Set): arkts.Statement[] { + const valueDecl = arkts.getDecl(value); + const result: arkts.Statement[] = []; + if (valueDecl instanceof arkts.ClassProperty) { + let varName = ((value as arkts.MemberExpression).property as arkts.Identifier).name; + let proxyName = stateProxy(varName); + let stateVar = (): arkts.TSNonNullExpression => createBackingFieldExpression(varName); + if (!proxySet.has(varName)) { + proxySet.add(varName); + const getProxy = getStateProxy(proxyName, stateVar); + result.push(getProxy); + } + const setParam = setPropertyESValue( + 'param', + keyName, + arkts.factory.createIdentifier(proxyName) + ); + result.push(setParam); + } else { + throw Error('unsupported data for Link'); + } + return result; +} + +/** + * + * @param keyName - The name of the state variable (e.g., state) + * @returns generate code to process regular data interoperability + */ +export function processNormal(keyName: string, value: arkts.AstNode): arkts.Statement[] { + const result: arkts.Statement[] = []; + if (arkts.isObjectExpression(value)) { + processObjectLiteral(value, InteroperAbilityNames.PARAM, result, keyName); + } else { + const setProperty = setPropertyESValue( + InteroperAbilityNames.PARAM, + keyName, + getWrapValue(value) + ); + result.push(setProperty); + } + return result; +} + +/** + * + * @param keyName + * @param value + * @returns generate code to process @BuilderParam interoperability + * @example + * Input: {builderParam: this.builder} + * Output: param.setProperty("builderParam", transferCompatibleBuilder(this.builder)); + */ +export function processBuilderParam(keyName: string, value: arkts.AstNode): arkts.Statement[] { + const result: arkts.Statement[] = []; + const newValue = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER), + undefined, + [ + value + ] + ); + const setProperty = setPropertyESValue( + InteroperAbilityNames.PARAM, + keyName, + newValue + ); + result.push(setProperty); + return result; +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop.ts b/arkui-plugins/ui-plugins/interop/interop.ts similarity index 36% rename from arkui-plugins/ui-plugins/interop.ts rename to arkui-plugins/ui-plugins/interop/interop.ts index d6c43d1c7d5c14fed395139969d516ae5d12ec13..8da4bc6d59e6567f8d924e9ead065ab5a57c86dc 100644 --- a/arkui-plugins/ui-plugins/interop.ts +++ b/arkui-plugins/ui-plugins/interop/interop.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Copyright (c) 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 @@ -16,155 +16,27 @@ import * as arkts from '@koalaui/libarkts'; -import { InteroperAbilityNames } from '../common/predefines'; -import { getCustomComponentOptionsName } from './utils'; -import { InteropContext } from './component-transformer'; -import { annotation, backingField, isAnnotation } from '../common/arkts-utils'; -import { hasLink, processLink, processNormal } from './initstatevar'; +import { BuilderMethodNames, ESValueMethodNames, InteroperAbilityNames } from './predefines'; +import { getCustomComponentOptionsName } from '../utils'; +import { InteropContext } from '../component-transformer'; +import { createVariableLet, initialArgs} from './initstatevar'; +import { + getPropertyESValue, + getWrapValue, + setPropertyESValue, + createEmptyESValue, + createGlobal, + createELMTID, + createInitReturn +} from './utils'; +import { ImportCollector } from '../../common/import-collector'; +import { factory as uiFactory } from '../ui-factory'; +import { DecoratorNames } from '../../common/predefines'; +import { hasDecorator } from '../property-translators/utils'; -interface propertyInfo { - decorators: string[], - type: arkts.TypeNode, -} - -export function createEmptyESValue(name: string): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(name), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.INITEMPTYOBJECT), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - ) - ] - ); -} - -export function getWrapValue(value: arkts.AstNode): arkts.AstNode { - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.WRAP), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [value] - ); -} - -export function setPropertyESValue(name: string, key: string, wrapValue: arkts.AstNode): arkts.ExpressionStatement { - return arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(name), - arkts.factory.createIdentifier(InteroperAbilityNames.SETPROPERTY), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - arkts.factory.createStringLiteral(key), - wrapValue - ] - ) - ); -} - -export function getPropertyESValue(result: string, object: string, key: string): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(result), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(object), - arkts.factory.createIdentifier(InteroperAbilityNames.GETPROPERTY), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.create1StringLiteral(key)] - ) - ) - ] - ); -} - -function initialArgs(args: arkts.ObjectExpression, varMap: Map): arkts.Statement[] { - const result: arkts.Statement[] = [ - createEmptyESValue(InteroperAbilityNames.PARAM), - getPropertyESValue('createState', 'global', 'createStateVariable') - ]; - - const proxySet = new Set(); - - for (const property of args.properties) { - if (!(property instanceof arkts.Property)) { - continue; - } - const key = property.key; - const value = property.value; - if (!(key instanceof arkts.Identifier)) { - throw Error('Error arguments in Legacy Component'); - } - const name = key.name; - const decorators = varMap.get(name)?.decorators; - const type = varMap.get(name)?.type!; - if (decorators !== undefined && hasLink(decorators)) { - const initParam = processLink(key.name, value!, type, proxySet); - result.push(...initParam); - } else { - const initParam = processNormal(key.name, value!); - result.push(...initParam); - } - } - return result; -} - -function instantiateComponent(params: arkts.AstNode[]): arkts.VariableDeclaration { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.STRUCTOBJECT), - arkts.factory.createIdentifier(InteroperAbilityNames.INSTANTIATE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - params - ) - ) - ] - ); -} function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): arkts.Statement[] { - const result = []; + const result: arkts.Statement[] = []; result.push( arkts.factory.createVariableDeclaration( arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, @@ -204,25 +76,6 @@ function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): a return result; } -function createInitReturn(componentName: string): arkts.ReturnStatement { - return arkts.factory.createReturnStatement( - arkts.ObjectExpression.createObjectExpression( - arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - [ - arkts.Property.createProperty( - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) - ), - arkts.Property.createProperty( - arkts.factory.createIdentifier('name'), - arkts.factory.createStringLiteral(componentName) - ) - ], - false - ), - ); -} - function createExtraInfo(properties: string[], value: string[]): arkts.Statement[] { const body: arkts.AstNode[] = []; body.push(createEmptyESValue(InteroperAbilityNames.EXTRAINFO)); @@ -237,183 +90,74 @@ function createExtraInfo(properties: string[], value: string[]): arkts.Statement return body; } -function createESParent(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esparent'), - getWrapValue(arkts.factory.createIdentifier(InteroperAbilityNames.PARENT)) - ) - ] - ); -} - -function createESUndefined(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esundefined'), - getWrapValue(arkts.factory.createUndefinedLiteral()) - ) - ] - ); -} - -function createESBlank(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const blank = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('blank'), - arkts.factory.createArrowFunction( - arkts.factory.createScriptFunction( - arkts.factory.createBlock([]), - arkts.factory.createFunctionSignature( - undefined, - [], - undefined, - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - ) - ) - ) - ] - ); - body.push(blank); - const asExpression = arkts.factory.createTSAsExpression( - arkts.factory.createIdentifier('blank'), +function generateTSASExpression(expression: arkts.AstNode): arkts.Expression { + return arkts.factory.createTSAsExpression( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + expression, + arkts.factory.createIdentifier('unwrap'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ), arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('object') + arkts.factory.createIdentifier('Object') ) ), false ); - const esblank = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('esblank'), - getWrapValue(asExpression) - ) - ] - ); - body.push(esblank); - return body; -} - -function createGlobal(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier('global'), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier('getGlobal'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); -} - -function createELMTID(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const viewStackProcessor = getPropertyESValue('viewStackProcessor', 'global', 'ViewStackProcessor'); - body.push(viewStackProcessor); - const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); - body.push(createId); - const elmtId = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('createId'), - arkts.factory.createIdentifier('invoke'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); - body.push(elmtId); - return body; } -function createComponent(moduleName: string, className: string): arkts.Statement[] { - const body: arkts.Statement[] = []; - const module = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(moduleName), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), - arkts.factory.createIdentifier(InteroperAbilityNames.LOAD), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, +function newComponent(className: string): arkts.Statement { + return createVariableLet( + InteroperAbilityNames.COMPONENT, + getWrapValue( + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createIdentifier(className), + [ + arkts.factory.createUndefinedLiteral(), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.PARAM)), + arkts.factory.createUndefinedLiteral(), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID)), + arkts.factory.createTSAsExpression( + arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock([]), + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Object') + ) + ), false ), - undefined, - [arkts.factory.create1StringLiteral(InteroperAbilityNames.OHMURL)] - ) + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO)) + ] ) - ] - ); - body.push(module); - const structObject = getPropertyESValue('structObject', moduleName, className); - body.push(structObject); - const component = instantiateComponent( - [ - arkts.factory.createIdentifier('esundefined'), - arkts.factory.createIdentifier(InteroperAbilityNames.PARAM), - arkts.factory.createIdentifier('esundefined'), - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), - arkts.factory.createIdentifier('esblank'), - arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO) - ] + ) ); - body.push(component); - return body; } -function invokeViewPUCreate(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const createMethod = getPropertyESValue('create', 'structObject', 'create'); - body.push(createMethod); - const viewPUCreate = arkts.factory.createExpressionStatement( +function createComponent(className: string): arkts.Statement[] { + const component = newComponent(className); + const ViewPU = getPropertyESValue('viewPUCreate', InteroperAbilityNames.GLOBAL, 'viewPUCreate'); + const create = arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('create'), + arkts.factory.createIdentifier('viewPUCreate'), arkts.factory.createIdentifier('invoke'), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, @@ -421,47 +165,43 @@ function invokeViewPUCreate(): arkts.Statement[] { ), undefined, [ - arkts.factory.createIdentifier('component') + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) ] ) ); - body.push(viewPUCreate); - return body; + return [component, ViewPU, create]; } -function createWrapperBlock(context: InteropContext, varMap: Map): arkts.BlockStatement { - const className = context.className; - const path = context.path; - const args = context.arguments; - const index = path.indexOf('/'); + +function createWrapperBlock(context: InteropContext, varMap: Map, + updateProp: arkts.Property[]): arkts.BlockStatement { + const className: string = context.className; + const path: string = context.path; + const args: arkts.ObjectExpression | undefined = context.arguments; + const index: number = path.indexOf('/'); if (index === -1) { throw new Error('Error path of Legacy Component.'); } - const moduleName = path.substring(0, index); - const initialArgsStatement = args ? initialArgs(args, varMap) : []; + const initial = [ + createGlobal(), + createEmptyESValue(InteroperAbilityNames.PARAM) + ]; + const initialArgsStatement = args ? initialArgs(args, varMap, updateProp) : []; return arkts.factory.createBlock( [ - createGlobal(), + ...initial, ...initialArgsStatement, ...createExtraInfo(['page'], [path]), - createESUndefined(), - ...createESBlank(), ...createELMTID(), - ...createComponent(moduleName, className), - ...invokeViewPUCreate(), - // ...paramsLambdaDeclaration(className, args), - // setPropertyESValue( - // 'component', - // 'paramsGenerator_', - // arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA) - // ), + ...createComponent(className), createInitReturn(className) ] ); } -function createInitializer(context: InteropContext, varMap: Map): arkts.ArrowFunctionExpression { - const block = createWrapperBlock(context, varMap); +function createInitializer(context: InteropContext, varMap: Map, + updateProp: arkts.Property[]): arkts.ArrowFunctionExpression { + const block = createWrapperBlock(context, varMap, updateProp); return arkts.factory.createArrowFunction( arkts.factory.createScriptFunction( block, @@ -477,19 +217,66 @@ function createInitializer(context: InteropContext, varMap: Map): arkts.ArrowFunctionExpression { +function createUpdateProp(updateProp: arkts.Property[]): arkts.Statement[] { + const result: arkts.Statement[] = []; + const updateParam = createEmptyESValue('updateParam'); + result.push(updateParam); + updateProp.forEach((prop) => { + const key = prop.key as arkts.Identifier; + const value = prop.value; + const insertProperty = setPropertyESValue('updateParam', key.name, value!); + result.push(insertProperty); + }); + return result; +} + +function updateStateVars(updateProp: arkts.Property[]): arkts.Statement[] { + const insertProp = createUpdateProp(updateProp); + return [ + ...insertProp, + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKEMETHOD), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral('updateStateVars'), + arkts.factory.createIdentifier('updateParam') + ] + ) + ]; +} + + +/** + * + * @param updateProp + * @returns (instance: ESValue) => { instance.invokeMethod('updateStateVars', updateParam) } + */ +function createUpdater(updateProp: arkts.Property[]): arkts.ArrowFunctionExpression { + const updateState = (updateProp.length !== 0) ? updateStateVars(updateProp) : []; return arkts.factory.createArrowFunction( arkts.factory.createScriptFunction( arkts.factory.createBlock( [ - + ...updateState ] ), arkts.factory.createFunctionSignature( undefined, [ arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, esvalue), + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ) + ), undefined, ), ], @@ -502,76 +289,91 @@ function createUpdater(esvalue: arkts.ETSTypeReference, varMap: Map { +function updateArguments(context: InteropContext, name: string): arkts.ObjectExpression { + return context.arguments ? arkts.factory.updateObjectExpression( + context.arguments, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + ...(context.arguments?.properties as arkts.Property[]), + arkts.factory.createProperty( + arkts.factory.createIdentifier(name), + arkts.factory.createTSAsExpression( + context.content, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Function') + ) + ), + false + ) + ) + ], + false + ) : arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.factory.createProperty( + arkts.factory.createIdentifier(name), + arkts.factory.createTSAsExpression( + context.content, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Function') + ) + ), + false + ) + ) + ], + false + ); +} + +function generateVarMap(context: InteropContext, node: arkts.Identifier): Map { + let needBuilderParam = !!context.content; const decl = arkts.getDecl(node); if (!(decl instanceof arkts.ClassDefinition)) { throw Error("can't find legacy class declaration"); } - const result = new Map(); + const result = new Map(); const definition = decl; const body = definition.body; body.forEach(node => { if (node instanceof arkts.ClassProperty && node.key instanceof arkts.Identifier) { const key = node.key.name; - const annotations = node.annotations; - const decorators: string[] = annotations.map(annotation => { - return (annotation.expr as arkts.Identifier).name; - }); - const type: arkts.TypeNode = node.typeAnnotation!; - result.set(key, {decorators: decorators, type: type}); + result.set(key, node); + if (needBuilderParam && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + context.arguments = updateArguments(context, key); + needBuilderParam = false; + } } }); return result; } -export function updateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { - const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; - const className = classInterop.name; - const args = node.arguments; - const path = (args[0] as arkts.StringLiteral).str; - const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; - const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; - const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; - const context: InteropContext = { - className: className, - path: path, - line: line, - col: col, - arguments: options - }; - - const varMap: Map = generateVarMap(classInterop); - const esvalue = arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) - ) - ); - const initializer = createInitializer(context, varMap); - const updater = createUpdater(esvalue, varMap); - return arkts.factory.updateCallExpression( - node, - arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), - undefined, - [ - initializer, - updater, - ] - ); -} - - function generateStructInfo(context: InteropContext): arkts.AstNode[] { const result: arkts.AstNode[] = [ arkts.factory.createStringLiteral(context.path), context.line ? arkts.factory.createIdentifier(context.line.toString()) : arkts.factory.createUndefinedLiteral(), context.col ? arkts.factory.createIdentifier(context.col.toString()) : arkts.factory.createUndefinedLiteral(), - context.arguments ?? arkts.factory.createUndefinedLiteral() + context.arguments ?? arkts.factory.createUndefinedLiteral(), + context.content ?? arkts.factory.createUndefinedLiteral(), ]; return result; } -export function generateTempCallFunction(context: InteropContext): arkts.CallExpression { +/** + * + * @param {Object} context - Context information about the parsed CustomComponent. + * @param {string} context.className - Name of the CustomComponent class. + * @param {string} context.path - File path where the CustomComponent is located. + * @param {number} [context.line] - Line number of the CustomComponent in the file (optional). + * @param {number} [context.col] - Column number of the CustomComponent in the file (optional). + * @param {Object} [context.arguments] - Additional arguments passed to the CustomComponent (optional). + * @returns {Object} .instantiate_Interop. + */ +export function generateInstantiateInterop(context: InteropContext): arkts.CallExpression { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( arkts.factory.createIdentifier(context.className), @@ -585,11 +387,64 @@ export function generateTempCallFunction(context: InteropContext): arkts.CallExp ); } +/** + * + * @param node + * @returns {boolean} Checks if a given CallExpression represents a call to .instantiate_Interop. + */ export function isArkUICompatible(node: arkts.AstNode): boolean { if (node instanceof arkts.CallExpression && node.expression instanceof arkts.MemberExpression && node.expression.property instanceof arkts.Identifier && node.expression.property.name === 'instantiate_Interop') { + ImportCollector.getInstance().collectSource(InteroperAbilityNames.ARKUICOMPATIBLE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.ARKUICOMPATIBLE); + ImportCollector.getInstance().collectSource(InteroperAbilityNames.GETCOMPATIBLESTATE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.GETCOMPATIBLESTATE); + ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER); return true; } return false; +} + + +/** + * + * @param node + * @returns After Checked, transform instantiate_Interop -> ArkUICompatible + */ +export function generateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { + const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; + const className = classInterop.name; + const args = node.arguments; + const path = (args[0] as arkts.StringLiteral).str; + const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; + const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; + const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; + const content = args[4] instanceof arkts.UndefinedLiteral ? undefined : args[4] as arkts.ArrowFunctionExpression; + const context: InteropContext = { + className: className, + path: path, + line: line, + col: col, + arguments: options, + content: content, + }; + + const varMap: Map = generateVarMap(context, classInterop); + const updateProp: arkts.Property[] = []; + const initializer = createInitializer(context, varMap, updateProp); + const updater = createUpdater(updateProp); + const result = arkts.factory.updateCallExpression( + node, + arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), + undefined, + [ + initializer, + updater, + arkts.factory.createThisExpression(), + ] + ); + arkts.NodeCache.getInstance().collect(result); + return result; } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts similarity index 55% rename from arkui-plugins/ui-plugins/legacy-transformer.ts rename to arkui-plugins/ui-plugins/interop/legacy-transformer.ts index 10047677ad11f81e98654faf738971ad3883be4a..1b831a9b00f81120621115d64aa85b81b2f64145 100644 --- a/arkui-plugins/ui-plugins/legacy-transformer.ts +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2025 Huawei Device Co., Ltd. + * Copyright (c) 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 @@ -13,21 +13,32 @@ * limitations under the License. */ + + import * as arkts from '@koalaui/libarkts'; -import { getInteropPath } from '../path'; +import { getInteropPath } from '../../path'; const interop = require(getInteropPath()); const nullptr = interop.nullptr; -import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; -import { InteroperAbilityNames } from '../common/predefines'; -import { getCustomComponentOptionsName } from './utils'; +import { AbstractVisitor, VisitorOptions } from '../../common/abstract-visitor'; +import { InteroperAbilityNames } from './predefines'; +import { getCustomComponentOptionsName } from '../utils'; +import { factory } from '../ui-factory'; interface LegacyTransformerOptions extends VisitorOptions { structList?: string[] } +type ScopeInfo = { + name: string; + isEntry?: boolean; + isComponent?: boolean; + isReusable?: boolean; +}; + export class LegacyTransformer extends AbstractVisitor { private structList: string[] = []; private componentInterfaceCollection: arkts.TSInterfaceDeclaration[] = []; + private scopeInfos: ScopeInfo[] = []; constructor(options?: LegacyTransformerOptions) { const _options: LegacyTransformerOptions = options ?? {}; @@ -35,9 +46,12 @@ export class LegacyTransformer extends AbstractVisitor { this.structList = _options.structList ?? []; } + // TODO: check reset reset(): void { super.reset(); + this.structList = []; this.componentInterfaceCollection = []; + this.scopeInfos = []; } getList(): string[] { @@ -66,12 +80,14 @@ export class LegacyTransformer extends AbstractVisitor { col.setOptional(true); const options = this.createParam('options', getCustomComponentOptionsName(name)); options.setOptional(true); + const trailingBlock = this.createParam('trailingBlock', 'Object'); + trailingBlock.setOptional(true); const script = arkts.factory.createScriptFunction( arkts.factory.createBlock([]), arkts.FunctionSignature.createFunctionSignature( undefined, - [path, line, col, options], + [path, line, col, options, trailingBlock], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -111,7 +127,7 @@ export class LegacyTransformer extends AbstractVisitor { const interfaceNode = arkts.factory.createInterfaceDeclaration( [], arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), - nullptr, // TODO: wtf + nullptr, arkts.factory.createInterfaceBody([...(this.generateMember(map) || [])]), false, false @@ -142,13 +158,10 @@ export class LegacyTransformer extends AbstractVisitor { definition.super, [...definition.body, instantiate_Interop], definition.modifiers, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaLanguage.JS ); - console.log('print legacyclass definition' + newDefinition.dumpSrc()); - - - //TODO: need check if (arkts.isStructDeclaration(node)) { const _node = arkts.factory.createClassDeclaration(newDefinition); _node.modifiers = node.modifiers; @@ -159,44 +172,45 @@ export class LegacyTransformer extends AbstractVisitor { } processConstructor(node: arkts.MethodDefinition): arkts.MethodDefinition { - const esvalue = arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) - ) - ); - const script = arkts.factory.createScriptFunction( - arkts.factory.createBlock([]), - arkts.factory.createFunctionSignature( - undefined, - [ - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('localStorage', esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, esvalue), - undefined, - ), - arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, esvalue), - undefined, - ) - ], undefined, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - ); + const valueType = arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Object') + ) + ), + arkts.factory.createETSUndefinedType() + ]); + const script = factory.createScriptFunction({ + body: arkts.factory.createBlock([]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('localStorage', valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, valueType), + undefined, + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, valueType), + undefined, + ) + ], + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }) return arkts.factory.updateMethodDefinition( node, node.kind, @@ -230,25 +244,103 @@ export class LegacyTransformer extends AbstractVisitor { return node; } + enter(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node) && !!node.definition.ident) { + const scopeInfo: ScopeInfo = { name: node.definition.ident.name }; + this.scopeInfos.push(scopeInfo); + } + } + + exit(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node) || arkts.isClassDeclaration(node)) { + 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(); + } + } + } + + handleWrappedBuilderNode(node: arkts.ETSTypeReference): arkts.ETSTypeReference { + if (node.part && arkts.isETSTypeReferencePart(node.part) && node.part.name && + arkts.isIdentifier(node.part.name) && node.part.name.name === 'WrappedBuilder') { + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Any') + ) + ); + } + return node; + } + + // handle WrappedBuilder + handleWrappedBuilder(node: arkts.VariableDeclarator): arkts.VariableDeclarator { + if (arkts.isIdentifier(node.name) && node.name.typeAnnotation) { + let typeAnnotation = node.name.typeAnnotation; + // WrappedBuilder<[aa]>[] => Any[] + if (arkts.isTSArrayType(typeAnnotation) && typeAnnotation.elementType && + arkts.isETSTypeReference(typeAnnotation.elementType)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + arkts.TSArrayType.updateTSArrayType( + typeAnnotation, + this.handleWrappedBuilderNode(typeAnnotation.elementType) + ) + ), + node.initializer + ); + } + // WrappedBuilder<[aa]> => Any + if (arkts.isETSTypeReference(typeAnnotation)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + this.handleWrappedBuilderNode(typeAnnotation) + ), + node.initializer + ); + } + } + return node; + } + visitor(node: arkts.AstNode): arkts.AstNode { + this.enter(node); const newNode = this.visitEachChild(node); if (arkts.isEtsScript(newNode)) { return this.processEtsScript(newNode); } if (arkts.isStructDeclaration(newNode)) { - const className = node.definition?.ident?.name; - const memberMap = this.collectComponentMembers(node as arkts.StructDeclaration, className); + const definition = newNode.definition!; + const annotations = definition.annotations; + if (annotations.some(annotation => annotation instanceof arkts.Identifier && annotation.name === 'Component')) { + return newNode; + } + const className = newNode.definition?.ident?.name!; + const memberMap = this.collectComponentMembers(newNode as arkts.StructDeclaration, className); this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers, memberMap)); const updateNode = this.processComponent(newNode); + this.exit(newNode); return updateNode; } - if (arkts.isMethodDefinition(newNode)) { + if (this.scopeInfos.length > 0 && arkts.isMethodDefinition(newNode)) { const kind = newNode.kind; if (kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR) { const updateNode = this.processConstructor(newNode); return updateNode; } } + if (arkts.isVariableDeclarator(newNode)) { + return this.handleWrappedBuilder(newNode); + } return newNode; } } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/predefines.ts b/arkui-plugins/ui-plugins/interop/predefines.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a8f5deabafc0a57c08b0e1d2eedba62543646fd --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/predefines.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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. + */ + + +export enum InteroperAbilityNames { + ARKTS_1_1 = '1.1', + ARKTS_1_2 = '1.2', + ARKUICOMPATIBLE = 'compatibleComponent', + ELMTID = 'elmtId', + NUMBER = 'number', + PARENT = 'parent', + INSTANCE = 'instance', + PARAM = 'param', + EXTRAINFO = 'extraInfo', + COMPONENT = 'component', + CONSTRUCTOR = 'constructor', + MODULE = 'module', + STRUCTOBJECT = 'structObject', + GLOBAL = 'global', + PARAMSLAMBDA = 'paramsLambda', + INTEROPCOMPONENT = 'interopComponent', + GETCOMPATIBLESTATE = 'getCompatibleState', + CREATESTATE = 'createStateVariable', + INTEROP = 'arkui.component.interop', +} + + +export enum ESValueMethodNames { + ESVALUE = 'ESValue', + INITEMPTYOBJECT = 'instantiateEmptyObject', + SETPROPERTY = 'setProperty', + GETPROPERTY = 'getProperty', + INSTANTIATE = 'instantiate', + INVOKE = 'invoke', + INVOKEMETHOD = 'invokeMethod', + LOAD = 'load', + WRAP = 'wrap', + WRAPINT = 'wrapInt', + WRAPSTRING = 'wrapString', + UNWRAP = 'unwrap', +} + +export enum InteropProvideNames { + STATICPROVIDE = 'provide', + FINDPROVIDE = 'findProvide', + PROVIDEDPROPNAME = 'providedPropName', + SETFINDPROVIDE = 'setFindProvideInterop', + SETVIEWPUFINDPROVIDE = 'setViewPUFindProvideInterop', + FINDPROVIDECALLBACK = 'findProvideInterop', +} + +export enum BuilderMethodNames { + RUNPENDINGJOBS = 'runPendingJobs', + CREATECOMPATIBLENODE = 'createCompatibleNode', + TRANSFERCOMPATIBLEBUILDER = 'transferCompatibleBuilder', +} + +export enum BuilderParams { + PARAM_WRAPPED_KEY = 'param_wrapped_key', + INSTANCEPARAM = 'instanceParam', + PARAM_WRAPPED = 'param_wrapped', + PARAM_WRAPPED_IT = 'param_wrapped_it' +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/utils.ts b/arkui-plugins/ui-plugins/interop/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..3dff5e7af5ae4b2ff609aef34db2154918b0f9fe --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/utils.ts @@ -0,0 +1,223 @@ +/* + * Copyright (c) 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 { ESValueMethodNames, InteroperAbilityNames } from './predefines'; + + +/** + * + * @param result + * @returns let result = ESValue.instantiateEmptyObject() + */ +export function createEmptyESValue(result: string): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier(ESValueMethodNames.INITEMPTYOBJECT), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ) + ] + ); +} + +/** + * + * @param value + * @returns ESValue.wrap(value) + */ +export function getWrapValue(value: arkts.AstNode): arkts.AstNode { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier(ESValueMethodNames.WRAP), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [value] + ); +} + +/** + * + * @param object + * @param key + * @param value + * @returns object.setProperty(key, value) + */ +export function setPropertyESValue(object: string, key: string, value: arkts.AstNode): arkts.ExpressionStatement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(object), + arkts.factory.createIdentifier(ESValueMethodNames.SETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral(key), + value.clone() + ] + ) + ); +} + +/** + * + * @param result + * @param obj + * @param key + * @returns let result = object.getProperty(key) + */ +export function getPropertyESValue(result: string, obj: string, key: string): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(obj), + arkts.factory.createIdentifier(ESValueMethodNames.GETPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [arkts.factory.create1StringLiteral(key)] + ) + ) + ] + ); +} + +/** + * + * @param {string} stateVarName - Original state variable name to be proxied. + * @returns {string} Proxied variable name in the format: "__Proxy_{stateVarName}". + */ +export function stateProxy(stateVarName: string): string { + return `__Proxy_${stateVarName}`; +} + +/** + * get elmtId + * @returns + * let viewStackProcessor = global.getProperty("ViewStackProcessor"); + * let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + * let elmtId = createId.invoke(); + */ +export function createELMTID(): arkts.Statement[] { + const body: arkts.Statement[] = []; + const viewStackProcessor = getPropertyESValue('viewStackProcessor', InteroperAbilityNames.GLOBAL, 'ViewStackProcessor'); + body.push(viewStackProcessor); + const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); + body.push(createId); + const elmtId = arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('createId'), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); + body.push(elmtId); + return body; +} + +/** + * + * @param componentName + * @returns return { + * component: component, + * name: componentName, + * }; + */ +export function createInitReturn(componentName: string): arkts.ReturnStatement { + return arkts.factory.createReturnStatement( + arkts.ObjectExpression.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.Property.createProperty( + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ), + arkts.Property.createProperty( + arkts.factory.createIdentifier('name'), + arkts.factory.createStringLiteral(componentName) + ) + ], + false + ), + ); +} + +/** + * createGlobal + * @returns let global = ESValue.getGlobal(); + */ +export function createGlobal(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.GLOBAL), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier('getGlobal'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); +} diff --git a/arkui-plugins/ui-plugins/preprocessor-transform.ts b/arkui-plugins/ui-plugins/preprocessor-transform.ts deleted file mode 100644 index 139a00d95f7c42aa537f0bd982eaf2de64ebecfd..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-plugins/preprocessor-transform.ts +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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 { CustomComponentNames } from './utils'; -import { factory } from './ui-factory'; -import { - ARKUI_COMPONENT_IMPORT_NAME, - IMPORT_SOURCE_MAP, - OUTPUT_DEPENDENCY_MAP, - ARKUI_STATEMANAGEMENT_IMPORT_NAME, - KIT_ARKUI_NAME, -} from '../common/predefines'; -import { NameCollector } from './name-collector'; - -interface MemoImportCollection { - memo: boolean; - memoContextType: boolean; - memoIdType: boolean; -} - -export class PreprocessorTransformer extends AbstractVisitor { - private outNameArr: string[] = []; - private memoNameArr: string[] = []; - private structInterfaceImport: arkts.ETSImportDeclaration[] = []; - private memoImportCollection: Partial = {}; - private localComponentNames: string[] = []; - private isMemoImportOnce: boolean = false; - - private readonly nameCollector: NameCollector; - - constructor(options?: VisitorOptions) { - super(options); - this.nameCollector = NameCollector.getInstance(); - } - - reset(): void { - super.reset(); - this.outNameArr = []; - this.memoNameArr = []; - this.structInterfaceImport = []; - this.memoImportCollection = {}; - this.localComponentNames = []; - this.isMemoImportOnce = false; - IMPORT_SOURCE_MAP.clear(); - IMPORT_SOURCE_MAP.set('arkui.stateManagement.runtime', new Set(['memo', '__memo_context_type', '__memo_id_type'])); - } - - isCustomConponentDecl(node: arkts.CallExpression): boolean { - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - const nodeName: string = node.expression.dumpSrc(); - if (structCollection.has(nodeName)) { - return true; - } - return false; - } - - isComponentFunctionCall(node: arkts.CallExpression): boolean { - if (!node || !arkts.isIdentifier(node.expression)) return false; - return this.localComponentNames.includes(node.expression.name); - } - - transformComponentCall(node: arkts.CallExpression): arkts.TSAsExpression | arkts.CallExpression { - if (node.arguments.length === 0 && node.trailingBlock) { - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - arkts.factory.createUndefinedLiteral(), - ]); - } else if (arkts.isObjectExpression(node.arguments[0])) { - const componentName: string = `${ - CustomComponentNames.COMPONENT_INTERFACE_PREFIX - }${node.expression.dumpSrc()}`; - const newArg = arkts.factory.createTSAsExpression( - node.arguments[0].clone(), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(componentName)) - ), - true - ); - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - newArg, - ...node.arguments.slice(1), - ]); - } else { - return node; - } - } - - transformComponentFunctionCall(node: arkts.CallExpression) { - if (!node || !arkts.isIdentifier(node.expression)) return node; - - const componentInfo = this.nameCollector.getComponentInfo(node.expression.name); - if (!componentInfo) return node; - if (componentInfo.argsNum === 0) return node; - if (node.arguments.length >= componentInfo.argsNum - 1) return node; - - const defaultArgs: arkts.UndefinedLiteral[] = []; - let count = 0; - while (count < componentInfo.argsNum - node.arguments.length - 1) { - defaultArgs.push(arkts.factory.createUndefinedLiteral()); - count++; - } - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - ...node.arguments, - ...defaultArgs, - ]); - } - - addDependencesImport(node: arkts.ETSImportDeclaration): void { - if (!node.source) return; - - const isFromCompImport: boolean = node.source.str === ARKUI_COMPONENT_IMPORT_NAME || node.source.str === KIT_ARKUI_NAME; - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - node.specifiers.forEach((item: arkts.AstNode) => { - if (!arkts.isImportSpecifier(item) || !item.imported?.name) return; - - const importName: string = item.imported.name; - this.memoImportCollection.memo ||= importName === 'memo'; - this.memoImportCollection.memoContextType ||= importName === '__memo_context_type'; - this.memoImportCollection.memoIdType ||= importName === '__memo_id_type'; - if (isFromCompImport && this.nameCollector.getComponents().includes(importName)) { - this.localComponentNames.push(item.local?.name ?? importName); - } - - if (structCollection.has(importName)) { - const interfaceName: string = CustomComponentNames.COMPONENT_INTERFACE_PREFIX + importName; - const newImport: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - node.source?.clone(), - [factory.createAdditionalImportSpecifier(interfaceName, interfaceName)], - arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, - this.program!, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - this.structInterfaceImport.push(newImport); - } else { - this.addImportWithSpecifier(item, node.source!); - } - }); - } - - getSourceDependency(sourceName: string): string { - let dependencyName: string = ''; - IMPORT_SOURCE_MAP.forEach((value: Set, key: string) => { - if (value.has(sourceName)) { - dependencyName = key; - } - }); - return dependencyName; - } - - updateSourceDependencyMap(key: string, value: string[]): void { - const newValue: Set = IMPORT_SOURCE_MAP.get(key) ?? new Set(); - for (const v of value) { - newValue.add(v); - } - IMPORT_SOURCE_MAP.set(key, newValue); - } - - getOutDependencyName(inputName: string): string[] { - const sourceName: string[] = []; - if (OUTPUT_DEPENDENCY_MAP.has(inputName)) { - OUTPUT_DEPENDENCY_MAP.get(inputName)!.forEach((item: string) => { - sourceName.push(item); - }); - } - return sourceName; - } - - updateOutDependencyMap(key: string, value: string[]): void { - const oldValue: string[] = OUTPUT_DEPENDENCY_MAP.get(key) ?? []; - const newValue: string[] = [...value, ...oldValue]; - OUTPUT_DEPENDENCY_MAP.set(key, newValue); - } - - clearGenSymInOutDependencyMap(genSymKey: string): void { - if (OUTPUT_DEPENDENCY_MAP.has(genSymKey)) { - OUTPUT_DEPENDENCY_MAP.delete(genSymKey); - } - } - - prepareDependencyMap(node: arkts.ImportSpecifier, source: arkts.StringLiteral): void { - if (!node.imported?.name) return; - - // Handling component imports - const importName: string = node.imported.name; - const sourceName: string = source.str; - if ( - this.nameCollector.getComponents().includes(importName) && - (sourceName === ARKUI_COMPONENT_IMPORT_NAME || sourceName === KIT_ARKUI_NAME) - ) { - const newDependencies = [`${importName}Attribute`]; - this.updateOutDependencyMap(importName, newDependencies); - this.updateSourceDependencyMap(sourceName, newDependencies); - } else if ( - OUTPUT_DEPENDENCY_MAP.get(importName) && - (sourceName === ARKUI_COMPONENT_IMPORT_NAME || - sourceName === ARKUI_STATEMANAGEMENT_IMPORT_NAME || - sourceName === KIT_ARKUI_NAME) - ) { - const newDependencies: string[] = OUTPUT_DEPENDENCY_MAP.get(importName) ?? []; - this.updateSourceDependencyMap(sourceName, newDependencies); - } - } - - prepareMemoImports(): void { - const newDependencies = []; - if (!this.memoImportCollection.memo) { - newDependencies.push('memo'); - } - if (!this.memoImportCollection.memoContextType) { - newDependencies.push('__memo_context_type'); - } - if (!this.memoImportCollection.memoIdType) { - newDependencies.push('__memo_id_type'); - } - if (newDependencies.length > 0) { - this.memoNameArr.push(...newDependencies); - this.isMemoImportOnce = true; - } - } - - addImportWithSpecifier(node: arkts.ImportSpecifier, source: arkts.StringLiteral): void { - if (!node.imported?.name) return; - - this.prepareDependencyMap(node, source); - const outName: string[] = this.getOutDependencyName(node.imported?.name); - this.outNameArr.push(...outName); - } - - updateScriptWithImport(): void { - if (!this.program) { - throw Error('Failed to insert import: Transformer has no program'); - } - - const outNames = new Set([...this.outNameArr, ...this.memoNameArr]); - outNames.forEach((item: string) => { - const source: string = this.getSourceDependency(item); - const newImport: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - arkts.factory.create1StringLiteral(source), - [factory.createAdditionalImportSpecifier(item, item)], - arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, - this.program!, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - arkts.importDeclarationInsert(newImport, this.program!); - }); - this.structInterfaceImport.forEach((element: arkts.ETSImportDeclaration) => { - arkts.importDeclarationInsert(element, this.program!); - }); - } - - enter(node: arkts.AstNode): void { - if (this.isExternal && arkts.isFunctionDeclaration(node)) { - const component = this.nameCollector.findComponentFunction(node); - if (!!component) this.nameCollector.collectInfoFromComponentFunction(component); - } - } - - visitor(node: arkts.AstNode): arkts.AstNode { - this.enter(node); - const newNode = this.visitEachChild(node); - if (arkts.isCallExpression(newNode) && this.isCustomConponentDecl(newNode)) { - return this.transformComponentCall(newNode); - } else if (arkts.isCallExpression(newNode) && this.isComponentFunctionCall(newNode)) { - return this.transformComponentFunctionCall(newNode); - } - if (arkts.isETSImportDeclaration(node)) { - this.addDependencesImport(node); - } else if (arkts.isEtsScript(node)) { - if (!this.isMemoImportOnce) this.prepareMemoImports(); - this.updateScriptWithImport(); - } - return newNode; - } -} diff --git a/arkui-plugins/ui-plugins/property-translators/base.ts b/arkui-plugins/ui-plugins/property-translators/base.ts index 4be00d1c8cca9781352e3477afcf32952e6aa32f..e79a7e7637cbcd5789860bdcf49e268291b3f602 100644 --- a/arkui-plugins/ui-plugins/property-translators/base.ts +++ b/arkui-plugins/ui-plugins/property-translators/base.ts @@ -14,55 +14,30 @@ */ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter, getStageManagementIdent } from './utils'; -import { createOptionalClassProperty } from '../utils'; +import { collectStateManagementTypeImport, createGetter, createSetter } from './utils'; +import { CustomComponentInfo, ClassInfo } from '../utils'; +import { StateManagementTypes } from '../../common/predefines'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { getClassPropertyType } from '../utils'; -export abstract class PropertyTranslator { - constructor( - protected property: arkts.ClassProperty, - protected structName: string - ) {} - - abstract translateMember(): arkts.AstNode[]; - - translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( - newName, - this.property, - getStageManagementIdent(this.property), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE - ); +export interface PropertyTranslatorOptions { + property: arkts.ClassProperty; + structInfo: CustomComponentInfo; +} - const member = arkts.factory.createTSNonNullExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ) - ); - const thisValue: arkts.MemberExpression = arkts.factory.createMemberExpression( - member, - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); +export abstract class PropertyTranslator { + protected property: arkts.ClassProperty; + protected structInfo: CustomComponentInfo; + protected propertyType: arkts.TypeNode | undefined; - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisValue - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisValue - ); - return [field, getter, setter]; + constructor(options: PropertyTranslatorOptions) { + this.property = options.property; + this.structInfo = options.structInfo; + this.propertyType = getClassPropertyType(options.property); } + abstract translateMember(): arkts.AstNode[]; + translateGetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, @@ -77,10 +52,65 @@ export abstract class PropertyTranslator { left: arkts.MemberExpression ): arkts.MethodDefinition { const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('observableProxy'), + arkts.factory.createIdentifier(StateManagementTypes.OBSERVABLE_PROXY), undefined, [arkts.factory.createIdentifier('value')] ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVABLE_PROXY); return createSetter(originalName, typeAnnotation, left, right); } } + +export abstract class MethodTranslator { + protected method: arkts.MethodDefinition; + protected classInfo: ClassInfo; + protected returnType: arkts.TypeNode | undefined; + + constructor(method: arkts.MethodDefinition, classInfo: ClassInfo) { + this.method = method; + this.classInfo = classInfo; + this.returnType = this.method.scriptFunction.returnTypeAnnotation?.clone(); + } + + abstract translateMember(): arkts.AstNode[]; +} + +export abstract class ObservedPropertyTranslator { + protected property: arkts.ClassProperty; + protected classScopeInfo: ClassScopeInfo; + protected propertyType: arkts.TypeNode | undefined; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + this.property = property; + this.classScopeInfo = classScopeInfo; + this.propertyType = getClassPropertyType(this.property); + } + + abstract translateMember(): arkts.AstNode[]; + abstract createField(originalName: string, newName: string): arkts.ClassProperty[]; +} + +export type InterfacePropertyTypes = arkts.MethodDefinition | arkts.ClassProperty; + +export interface InterfacePropertyTranslatorOptions { + property: T; +} + +export abstract class InterfacePropertyTranslator + implements InterfacePropertyTranslatorOptions +{ + property: T; + + modified: boolean; + + constructor(options: InterfacePropertyTranslatorOptions) { + this.property = options.property; + this.modified = false; + } + + abstract translateProperty(): T; + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + return false; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/builderParam.ts b/arkui-plugins/ui-plugins/property-translators/builderParam.ts index fbf600f066715a3f170c0f2bb9c9f07af511aedf..40b3a54ea1c4d3043f8fb38482805ca38170f3e3 100644 --- a/arkui-plugins/ui-plugins/property-translators/builderParam.ts +++ b/arkui-plugins/ui-plugins/property-translators/builderParam.ts @@ -15,51 +15,53 @@ import * as arkts from '@koalaui/libarkts'; -import { - createGetter, - createSetter, - generateThisBackingValue, - generateThisBacking, - getValueInAnnotation, - DecoratorNames, -} from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; +import { DecoratorNames } from '../../common/predefines'; +import { createGetter, createSetter, generateThisBacking, hasDecorator, removeDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; +import { PropertyCache } from './cache/propertyCache'; export class BuilderParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const mutableThis: arkts.Expression = generateThisBacking(newName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(mutableThis, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty(newName, this.property, '', - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, true); - const thisGetValue: arkts.Expression = generateThisBacking(newName, false, true); + const propertyType = this.propertyType; + if (!!propertyType && (arkts.isETSFunctionType(propertyType) || arkts.isETSUnionType(propertyType))) { + addMemoAnnotation(propertyType); + } + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + true + ); + arkts.NodeCache.getInstance().collect(field); const thisSetValue: arkts.Expression = generateThisBacking(newName, false, false); const getter: arkts.MethodDefinition = this.translateGetter( originalName, - this.property.typeAnnotation, - thisGetValue - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSetValue + propertyType, + arkts.hasModifierFlag(this.property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL) + ? generateThisBacking(newName, false, false) + : generateThisBacking(newName, false, true) ); + arkts.NodeCache.getInstance().collect(getter); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, propertyType, thisSetValue); + arkts.NodeCache.getInstance().collect(setter); return [field, getter, setter]; } @@ -82,6 +84,10 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia } generateInitializeStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const value = this.property.value; + if (!!value && arkts.isArrowFunctionExpression(value)) { + arkts.NodeCache.getInstance().collect(value); + } return arkts.factory.createAssignmentExpression( mutableThis, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, @@ -94,9 +100,77 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia arkts.factory.createIdentifier('content'), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), + value ?? arkts.factory.createUndefinedLiteral(), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ) ); } } + +export class BuilderParamInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateBuilderParamMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateBuilderParamPropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.BUILDER_PARAM)) { + return true; + } + return false; + } + + /** + * Add `@memo` to getter's return type and setter's param type (expecting a function type or a function type within a union type). + * + * @param method expecting getter with `@BuilderParam` and a setter with `@BuilderParam` in the overloads. + */ + private updateBuilderParamMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const type: arkts.TypeNode | undefined = method.scriptFunction.returnTypeAnnotation; + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { + addMemoAnnotation(type); + } + const newOverLoads = method.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return this.updateBuilderParamMethodInInterface(overload); + } + return overload; + }); + method.setOverloads(newOverLoads); + removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isGetter: true }); + } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const param = method.scriptFunction.params.at(0)! as arkts.ETSParameterExpression; + const type = param.type; + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { + addMemoAnnotation(type); + } + removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isSetter: true }); + } + return method; + } + + /** + * Add `@memo` to the type of the property (expecting a function type or a function type within a union type). + * + * @param property expecting property with `@BuilderParam`. + */ + private updateBuilderParamPropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + const type: arkts.TypeNode | undefined = property.typeAnnotation; + if (findCanAddMemoFromTypeAnnotation(type)) { + addMemoAnnotation(type); + } + removeDecorator(property, DecoratorNames.BUILDER_PARAM); + return property; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts b/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts new file mode 100644 index 0000000000000000000000000000000000000000..898d2aa21e2add45a3b7ab88d0032b4e637460ab --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/cache/monitorCache.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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 { factory } from '../factory'; + +export interface MonitorInfo { + monitorItem: string[] | undefined; + originalName: string; + newName: string; +} + +export class MonitorCache { + private _cache: Map>; + private static instance: MonitorCache | null = null; + + private constructor() { + this._cache = new Map>(); + } + + static getInstance(): MonitorCache { + if (!this.instance) { + this.instance = new MonitorCache(); + } + return this.instance; + } + + reset(): void { + this._cache.clear(); + } + + getCachedMonitors(className: string): arkts.AstNode[] { + if (!this._cache.has(className)) { + return []; + } + return Object.entries(this._cache.get(className)!).map((item: [string, MonitorInfo]) => { + const { monitorItem, originalName, newName } = item[1]; + return factory.generateinitAssignment(monitorItem, originalName, newName); + }); + } + + collectMonitors(className: string, monitorPath: string, info: MonitorInfo): void { + let classCache: Record = {}; + if (this._cache.has(className)) { + classCache = this._cache.get(className)!; + } + classCache[monitorPath] = info; + this._cache.set(className, classCache); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts b/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed76775865f04320b32bcf785d054bbec1d1c52c --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/cache/propertyCache.ts @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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'; + +export interface PropertyCachedBody { + initializeBody?: arkts.AstNode[]; + updateBody?: arkts.AstNode[]; + toRecordBody?: arkts.Property[]; + constructorBody?: arkts.AstNode[]; + monitorBody?: arkts.AstNode[]; +} + +export class PropertyCache { + private _cache: Map; + private static instance: PropertyCache; + + private constructor() { + this._cache = new Map(); + } + + static getInstance(): PropertyCache { + if (!this.instance) { + this.instance = new PropertyCache(); + } + return this.instance; + } + + reset(): void { + this._cache.clear(); + } + + getInitializeBody(name: string): arkts.AstNode[] { + return this._cache.get(name)?.initializeBody ?? []; + } + + getUpdateBody(name: string): arkts.AstNode[] { + return this._cache.get(name)?.updateBody ?? []; + } + + getToRecordBody(name: string): arkts.Property[] { + return this._cache.get(name)?.toRecordBody ?? []; + } + + collectInitializeStruct(name: string, initializeStruct: arkts.AstNode[]): void { + const initializeBody = this._cache.get(name)?.initializeBody ?? []; + const newInitializeBody = [...initializeBody, ...initializeStruct]; + this._cache.set(name, { ...this._cache.get(name), initializeBody: newInitializeBody }); + } + + collectUpdateStruct(name: string, updateStruct: arkts.AstNode[]): void { + const updateBody = this._cache.get(name)?.updateBody ?? []; + const newUpdateBody = [...updateBody, ...updateStruct]; + this._cache.set(name, { ...this._cache.get(name), updateBody: newUpdateBody }); + } + + collectToRecord(name: string, toRecord: arkts.Property[]): void { + const toRecordBody = this._cache.get(name)?.toRecordBody ?? []; + const newToRecordBody = [...toRecordBody, ...toRecord]; + this._cache.set(name, { ...this._cache.get(name), toRecordBody: newToRecordBody }); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/computed.ts b/arkui-plugins/ui-plugins/property-translators/computed.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e5cc573ae2be4963a059f43fc84770b530ca3e9 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/computed.ts @@ -0,0 +1,88 @@ +/* + * Copyright (c) 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 { expectName } from '../../common/arkts-utils'; +import { GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { ClassInfo, computedField } from '../utils'; +import { generateThisBacking, generateGetOrSetCall, getGetterReturnType } from './utils'; +import { MethodTranslator } from './base'; +import { InitializerConstructor } from './types'; +import { factory as UIFactory } from '../ui-factory'; +import { factory } from './factory'; + +export class ComputedTranslator extends MethodTranslator implements InitializerConstructor { + private isStatic: boolean; + + constructor(method: arkts.MethodDefinition, classInfo: ClassInfo) { + super(method, classInfo); + this.isStatic = this.method.isStatic; + } + + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.method.name); + const newName: string = computedField(originalName); + if (!this.returnType) { + this.returnType = getGetterReturnType(this.method); + } + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string): void {} + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const modifiers = this.isStatic ? this.method.modifiers : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE; + const field: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_COMPUTED, + this.returnType, + [ + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: this.method.scriptFunction.body?.clone(), + modifiers: modifiers, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ), + arkts.factory.createStringLiteral(originalName), + ], + false + ), + undefined, + modifiers, + false + ); + + const originGetter: arkts.MethodDefinition = UIFactory.updateMethodDefinition(this.method, { + function: { + returnTypeAnnotation: this.returnType, + body: arkts.factory.createBlock([ + arkts.factory.createReturnStatement(this.generateComputedGet(newName)), + ]), + }, + }); + + return [field, originGetter]; + } + + generateComputedGet(newName: string): arkts.CallExpression { + const thisValue: arkts.Expression = this.isStatic + ? UIFactory.generateMemberExpression(arkts.factory.createIdentifier(this.classInfo.className), newName) + : generateThisBacking(newName, false, true); + return generateGetOrSetCall(thisValue, GetSetTypes.GET); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/consume.ts b/arkui-plugins/ui-plugins/property-translators/consume.ts index bbb32deb1c9ffdfe306a05bf50c910e0843480c9..52f38c6fa53ffe1f7668a1aa7d8d67c8d846b4cd 100644 --- a/arkui-plugins/ui-plugins/property-translators/consume.ts +++ b/arkui-plugins/ui-plugins/property-translators/consume.ts @@ -15,6 +15,8 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; import { generateToRecord, createGetter, @@ -22,55 +24,44 @@ import { generateThisBacking, generateGetOrSetCall, getValueInAnnotation, - DecoratorNames, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class ConsumeTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ConsumeDecoratedVariable', + StateManagementTypes.CONSUME_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -92,12 +83,60 @@ export class ConsumeTranslator extends PropertyTranslator implements Initializer } generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { - const alias = getValueInAnnotation(this.property, DecoratorNames.CONSUME); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.CONSUME) ?? originalName + ), + ]; + factory.judgeIfAddWatchFunc(args, this.property); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.generateInitConsumeCall(originalName, this.property, alias ?? originalName) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_CONSUME, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class ConsumeInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.CONSUME)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.CONSUME)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ConsumeDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Consume` and a setter with `@Consume` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.CONSUME); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ConsumeDecoratedVariable | undefined`. + * + * @param property expecting property with `@Consume`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.CONSUME); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/consumer.ts b/arkui-plugins/ui-plugins/property-translators/consumer.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccfb63461aebc739a052439144d73cf48c5aad62 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/consumer.ts @@ -0,0 +1,137 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + getValueInAnnotation, + hasDecorator, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ConsumerTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.CONSUMER_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.CONSUMER) ?? originalName + ), + this.property.value!, + ]; + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_CONSUMER, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class ConsumerInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.CONSUMER)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.CONSUMER)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IConsumerDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Consumer` and a setter with `@Consumer` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.CONSUMER); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IConsumerDecoratedVariable | undefined`. + * + * @param property expecting property with `@Consumer`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.CONSUMER); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/factory.ts b/arkui-plugins/ui-plugins/property-translators/factory.ts index c1f610a4bf9c4ff43b0b5286ac50f8432f349c57..b9bcba9a7d60ae5e48fb573d5f43b3b20c9274be 100644 --- a/arkui-plugins/ui-plugins/property-translators/factory.ts +++ b/arkui-plugins/ui-plugins/property-translators/factory.ts @@ -15,8 +15,26 @@ import * as arkts from '@koalaui/libarkts'; import { GenSymGenerator } from '../../common/gensym-generator'; +import { + DecoratorNames, + DECORATOR_TYPE_MAP, + StateManagementTypes, + ObservedNames, + MonitorNames, + TypeNames, +} from '../../common/predefines'; import { factory as UIFactory } from '../ui-factory'; -import { judgeIfAddWatchFunc } from './utils'; +import { + collectStateManagementTypeImport, + generateThisBacking, + getValueInAnnotation, + hasDecorator, + OptionalMemberInfo, + removeDecorator, +} from './utils'; +import { CustomComponentNames, getClassPropertyType } from '../utils'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; +import { annotation, isNumeric } from '../../common/arkts-utils'; export class factory { /** @@ -24,16 +42,17 @@ export class factory { * * @param object item before ?.. * @param key item after ?.. + * @param info optional member information */ static createBlockStatementForOptionalExpression( object: arkts.AstNode, key: string, - isCall: boolean = false + info?: OptionalMemberInfo ): arkts.Expression { let id = GenSymGenerator.getInstance().id(key); const statements: arkts.Statement[] = [ factory.generateLetVariableDecl(arkts.factory.createIdentifier(id), object), - factory.generateTernaryExpression(id, key, isCall), + factory.generateTernaryExpression(id, key, info), ]; return arkts.factory.createBlockExpression(statements); } @@ -67,7 +86,7 @@ export class factory { static generateTernaryExpression( testLeft: string, key: string, - isCall: boolean = false + info?: OptionalMemberInfo ): arkts.ExpressionStatement { const test = arkts.factory.createBinaryExpression( arkts.factory.createIdentifier(testLeft), @@ -75,22 +94,30 @@ export class factory { arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_EQUAL ); const consequent: arkts.Expression = arkts.factory.createUndefinedLiteral(); - const alternate: arkts.MemberExpression = arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(testLeft), - arkts.factory.createIdentifier(key), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); return arkts.factory.createExpressionStatement( arkts.factory.createConditionalExpression( test, consequent, - isCall ? arkts.factory.createCallExpression(alternate, undefined, undefined) : alternate + this.generateConditionalAlternate(testLeft, key, info) ) ); } + static generateConditionalAlternate(testLeft: string, key: string, info?: OptionalMemberInfo): arkts.Expression { + const leftIdent: arkts.Identifier = arkts.factory.createIdentifier(testLeft); + const alternate: arkts.MemberExpression = UIFactory.generateMemberExpression( + leftIdent, + info?.isNumeric ? '$_get' : key + ); + return info?.isCall + ? arkts.factory.createCallExpression(alternate, undefined, undefined) + : info?.isNumeric + ? arkts.factory.createCallExpression(alternate, undefined, [ + arkts.factory.createNumericLiteral(Number(key)), + ]) + : alternate; + } + /** * generate an substitution for two optional expression ?., e.g. a?.b?.c. * @@ -168,7 +195,7 @@ export class factory { false, [ arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(factory.generateThisCall(callbackName), undefined, [ + arkts.factory.createCallExpression(generateThisBacking(callbackName), undefined, [ arkts.factory.createIdentifier('_'), ]) ), @@ -176,20 +203,6 @@ export class factory { ); } - /* - * create this. with optional or nonNullable. - */ - static generateThisCall(name: string, optional: boolean = false, nonNull: boolean = false): arkts.Expression { - const member: arkts.Expression = arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(`${name}`), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - optional - ); - return nonNull ? arkts.factory.createTSNonNullExpression(member) : member; - } - /* * create `initializers!.!.()`. */ @@ -233,64 +246,658 @@ export class factory { } /* - * create `this.addProvidedVar(, , initializers?. ?? , , watchFunc)`. + * create `StateMgmtFactory.(this, ...);`. */ - static generateAddProvideVarCall( + static generateStateMgmtFactoryCall( + makeType: StateManagementTypes, + typeArguments: arkts.TypeNode | undefined, + args: arkts.AstNode[], + argsContainsThis: boolean + ): arkts.CallExpression { + collectStateManagementTypeImport(StateManagementTypes.STATE_MANAGEMENT_FACTORY); + return arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.STATE_MANAGEMENT_FACTORY), + makeType + ), + typeArguments ? [typeArguments] : undefined, + [...(argsContainsThis ? [arkts.factory.createThisExpression()] : []), ...args] + ); + } + + /* + * create if statement in __updateStruct method. + */ + static createIfInUpdateStruct( originalName: string, + member: arkts.Expression, + args: arkts.AstNode[] + ): arkts.IfStatement { + const binaryItem = arkts.factory.createBinaryExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ), + arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL + ); + return arkts.factory.createIfStatement( + binaryItem, + arkts.factory.createBlock([ + arkts.factory.createExpressionStatement(arkts.factory.createCallExpression(member, undefined, args)), + ]) + ); + } + + /* + * create `initializers!. as `. + */ + static generateDefiniteInitializers(type: arkts.TypeNode | undefined, originalName: string): arkts.Expression { + return arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + type ? type.clone() : undefined, + false + ); + } + + static judgeIfAddWatchFunc(args: arkts.Expression[], property: arkts.ClassProperty): void { + if (hasDecorator(property, DecoratorNames.WATCH)) { + const watchStr: string | undefined = getValueInAnnotation(property, DecoratorNames.WATCH); + if (watchStr) { + args.push(factory.createWatchCallback(watchStr)); + } + } + } + + static createOptionalClassProperty( + name: string, property: arkts.ClassProperty, - alias: string, - allowOverride: boolean = false - ): arkts.CallExpression { - const args: arkts.Expression[] = [ - arkts.factory.create1StringLiteral(originalName), - arkts.factory.create1StringLiteral(alias), - arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName + stageManagementType: StateManagementTypes | undefined, + modifiers: arkts.Es2pandaModifierFlags, + needMemo: boolean = false + ): arkts.ClassProperty { + const originType = getClassPropertyType(property); + const newType: arkts.TypeNode | undefined = !stageManagementType + ? property.typeAnnotation ?? UIFactory.createTypeReferenceFromString(TypeNames.ANY) + : originType; + if (needMemo && findCanAddMemoFromTypeAnnotation(newType)) { + addMemoAnnotation(newType); + } + const newProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(name), + undefined, + !!stageManagementType ? factory.createStageManagementType(stageManagementType, originType) : newType, + modifiers, + false + ); + return arkts.classPropertySetOptional(newProperty, true); + } + + static createStageManagementType( + stageManagementType: StateManagementTypes, + type: arkts.TypeNode | undefined + ): arkts.ETSTypeReference { + collectStateManagementTypeImport(stageManagementType); + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(stageManagementType), + arkts.factory.createTSTypeParameterInstantiation([type ?? arkts.factory.createETSUndefinedType()]) + ) + ); + } + + /* + * create watch related members in Observed/Track classes + */ + static createWatchMembers(): arkts.AstNode[] { + const subscribedWatches: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier('subscribedWatches'), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_SUBSCRIBED_WATCHES, undefined, [], false), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.SUBSCRIBED_WATCHES) + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + subscribedWatches.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + collectStateManagementTypeImport(StateManagementTypes.SUBSCRIBED_WATCHES); + + const addWatchSubscriber = factory.createWatchMethod( + 'addWatchSubscriber', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + 'watchId', + StateManagementTypes.WATCH_ID_TYPE, + false + ); + const removeWatchSubscriber = factory.createWatchMethod( + 'removeWatchSubscriber', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN, + 'watchId', + StateManagementTypes.WATCH_ID_TYPE, + true + ); + collectStateManagementTypeImport(StateManagementTypes.WATCH_ID_TYPE); + + const executeOnSubscribingWatches = factory.createWatchMethod( + 'executeOnSubscribingWatches', + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + 'propertyName', + 'string', + false + ); + + return [subscribedWatches, addWatchSubscriber, removeWatchSubscriber, executeOnSubscribingWatches]; + } + + /* + * helper for createWatchMembers to create watch methods + */ + static createWatchMethod( + methodName: string, + returnType: arkts.Es2pandaPrimitiveType, + paramName: string, + paramType: string, + isReturnStatement: boolean + ): arkts.MethodDefinition { + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(methodName), + arkts.factory.createScriptFunction( + arkts.factory.createBlock([ + isReturnStatement + ? arkts.factory.createReturnStatement( + arkts.factory.createCallExpression( + factory.thisSubscribedWatchesMember(methodName), + undefined, + [arkts.factory.createIdentifier(paramName)] + ) + ) + : arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + factory.thisSubscribedWatchesMember(methodName), + undefined, + [arkts.factory.createIdentifier(paramName)] + ) + ), + ]), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + paramName, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(paramType)) + ) + ), + undefined + ), + ], + arkts.factory.createPrimitiveType(returnType), + false ), - property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ), - arkts.factory.createBooleanLiteral(allowOverride), - ]; - judgeIfAddWatchFunc(args, property); - return arkts.factory.createCallExpression( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /* + * helper for createWatchMethod, generates this.subscribedWatches.xxx + */ + static thisSubscribedWatchesMember(member: string): arkts.MemberExpression { + return arkts.factory.createMemberExpression( arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('addProvidedVar'), + arkts.factory.createIdentifier('subscribedWatches'), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ), - property.typeAnnotation ? [property.typeAnnotation.clone()] : undefined, - args + arkts.factory.createIdentifier(member), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false ); } /* - * create `this.initConsume(, , watchFunc)`. + * create ____V1RenderId related members in Observed/Track classes */ - static generateInitConsumeCall( - originalName: string, + static createV1RenderIdMembers(isObservedV2: boolean): arkts.AstNode[] { + const v1RenderId: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.V1_RERENDER_ID), + arkts.factory.createNumericLiteral(0), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.RENDER_ID_TYPE) + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + v1RenderId.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + collectStateManagementTypeImport(StateManagementTypes.RENDER_ID_TYPE); + const setV1RenderId: arkts.MethodDefinition = factory.setV1RenderId(isObservedV2); + return isObservedV2 ? [setV1RenderId] : [v1RenderId, setV1RenderId]; + } + + /* + * helper for createV1RenderIdMembers to generate setV1RenderId method + */ + static setV1RenderId(isObservedV2: boolean): arkts.MethodDefinition { + const assignRenderId: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + generateThisBacking(ObservedNames.V1_RERENDER_ID), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(ObservedNames.RERENDER_ID) + ) + ); + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(ObservedNames.SET_V1_RERENDER_ID), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock(isObservedV2 ? [] : [assignRenderId]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + ObservedNames.RERENDER_ID, + UIFactory.createTypeReferenceFromString(StateManagementTypes.RENDER_ID_TYPE) + ), + undefined + ), + ], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); + } + + /* + * create conditionalAddRef method in Observed/Track classes + */ + static conditionalAddRef(isObservedV2: boolean): arkts.MethodDefinition { + const metaAddRef: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(ObservedNames.META), + ObservedNames.ADD_REF + ), + undefined, + undefined + ) + ); + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(ObservedNames.CONDITIONAL_ADD_REF), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock(isObservedV2 ? [metaAddRef] : [factory.shouldAddRef(metaAddRef)]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + ObservedNames.META, + UIFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META) + ), + undefined + ), + ], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + }); + } + + /* + * helper for conditionalAddRef to generate shouldAddRef method + */ + static shouldAddRef(metaAddRef: arkts.ExpressionStatement): arkts.IfStatement { + const test: arkts.CallExpression = arkts.factory.createCallExpression( + UIFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.OBSERVE), + ObservedNames.SHOULD_ADD_REF + ), + undefined, + [generateThisBacking(ObservedNames.V1_RERENDER_ID)] + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVE); + const consequent: arkts.BlockStatement = arkts.factory.createBlock([metaAddRef]); + return arkts.factory.createIfStatement(test, consequent); + } + + /* + * helper to create meta field in classes with only @Observe and no @Track + */ + static createMetaInObservedClass(): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + const meta = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(StateManagementTypes.META), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + UIFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + meta.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + return meta; + } + + /** + * add `@memo` to the `@Builder` methods in class. + */ + static addMemoToBuilderClassMethod(method: arkts.MethodDefinition): arkts.MethodDefinition { + if (hasDecorator(method, DecoratorNames.BUILDER)) { + removeDecorator(method, DecoratorNames.BUILDER); + addMemoAnnotation(method.scriptFunction); + } + return method; + } + + static createStorageLinkStateValue( property: arkts.ClassProperty, - alias: string - ): arkts.CallExpression { - const args: arkts.Expression[] = [ - arkts.factory.create1StringLiteral(originalName), - arkts.factory.create1StringLiteral(alias), - ]; - judgeIfAddWatchFunc(args, property); - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('initConsume'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + localStorageporpValueStr: string + ): arkts.MemberExpression { + return arkts.factory.createMemberExpression( + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), + property.typeAnnotation ? [property.typeAnnotation] : [], + [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('_entry_local_storage_'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createStringLiteral(localStorageporpValueStr), + property.value ?? arkts.factory.createUndefinedLiteral(), + ] ), - property.typeAnnotation ? [property.typeAnnotation.clone()] : undefined, - args + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + } + + /** + * wrap interface non-undefined property type `T` to ``. + */ + static wrapInterfacePropertyType(type: arkts.TypeNode, wrapTypeName: StateManagementTypes): arkts.TypeNode { + if (arkts.isETSUnionType(type)) { + return arkts.factory.updateUnionType(type, [ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(wrapTypeName), + arkts.factory.createTSTypeParameterInstantiation([type.types[0]]) + ) + ), + type.types[1], + ]); + } + return type; + } + + /** + * wrap interface property parameter that has non-undefined type `T` to ``. + */ + static wrapInterfacePropertyParamExpr( + param: arkts.Expression, + wrapTypeName: StateManagementTypes + ): arkts.Expression { + if (!arkts.isEtsParameterExpression(param)) { + return param; + } + if (!param.type || !arkts.isETSUnionType(param.type)) { + return param; + } + return arkts.factory.updateParameterDeclaration( + param, + arkts.factory.createIdentifier( + param.identifier.name, + factory.wrapInterfacePropertyType(param.type, wrapTypeName) + ), + param.initializer + ); + } + + static wrapStateManagementTypeToType( + type: arkts.TypeNode | undefined, + decoratorName: DecoratorNames + ): arkts.TypeNode | undefined { + let newType: arkts.TypeNode | undefined; + let wrapTypeName: StateManagementTypes | undefined; + if (!!type && !!(wrapTypeName = DECORATOR_TYPE_MAP.get(decoratorName))) { + newType = factory.wrapInterfacePropertyType(type, wrapTypeName); + collectStateManagementTypeImport(wrapTypeName); + } + return newType; + } + + static wrapStateManagementTypeToParam( + param: arkts.Expression | undefined, + decoratorName: DecoratorNames + ): arkts.Expression | undefined { + let newParam: arkts.Expression | undefined; + let wrapTypeName: StateManagementTypes | undefined; + if (!!param && !!(wrapTypeName = DECORATOR_TYPE_MAP.get(decoratorName))) { + newParam = factory.wrapInterfacePropertyParamExpr(param, wrapTypeName); + collectStateManagementTypeImport(wrapTypeName); + } + return newParam; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to ` | undefined`, where `` is getting from `DecoratorName`; + * + * @param method expecting getter with decorator annotation and a setter with decorator annotation in the overloads. + */ + static wrapStateManagementTypeToMethodInInterface( + method: arkts.MethodDefinition, + decorator: DecoratorNames + ): arkts.MethodDefinition { + if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const newType: arkts.TypeNode | undefined = factory.wrapStateManagementTypeToType( + method.scriptFunction.returnTypeAnnotation, + decorator + ); + const newOverLoads = method.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return factory.wrapStateManagementTypeToMethodInInterface(overload, decorator); + } + return overload; + }); + method.setOverloads(newOverLoads); + removeDecorator(method, decorator); + if (!!newType) { + method.scriptFunction.setReturnTypeAnnotation(newType); + } + } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const newParam: arkts.Expression | undefined = factory.wrapStateManagementTypeToParam( + method.scriptFunction.params.at(0), + decorator + ); + removeDecorator(method, decorator); + if (!!newParam) { + return UIFactory.updateMethodDefinition(method, { function: { params: [newParam] } }); + } + } + return method; + } + + /** + * create external assignment node, e.g. `initializers?. ?? .value` or `initializers!.!`. + * + * @param property class property node. + * @param propertyType class property type. + * @param originalName property name. + */ + static generateInitializeValue( + property: arkts.ClassProperty, + propertyType: arkts.TypeNode | undefined, + originalName: string + ): arkts.Expression { + const outInitialize: arkts.Expression = factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ); + const binaryItem: arkts.Expression = arkts.factory.createBinaryExpression( + outInitialize, + property.value ?? arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + ); + const finalBinary: arkts.Expression = property.typeAnnotation + ? binaryItem + : arkts.factory.createTSAsExpression(binaryItem, propertyType, false); + return property.value ? finalBinary : factory.generateDefiniteInitializers(propertyType, originalName); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to ` | undefined`, where `` is getting from `DecoratorName`; + * + * @param property expecting property with decorator annotation. + */ + static wrapStateManagementTypeToPropertyInInterface( + property: arkts.ClassProperty, + decorator: DecoratorNames + ): arkts.ClassProperty { + const newType: arkts.TypeNode | undefined = factory.wrapStateManagementTypeToType( + property.typeAnnotation, + decorator + ); + removeDecorator(property, decorator); + if (!!newType) { + property.setTypeAnnotation(newType); + } + return property; + } + + static generateinitAssignment( + monitorItem: string[] | undefined, + originalName: string, + newName: string + ): arkts.ExpressionStatement { + const thisValue: arkts.Expression = generateThisBacking(newName, false, false); + const right: arkts.CallExpression = factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_MONITOR, + undefined, + [this.generatePathArg(monitorItem), this.generateLambdaArg(originalName)], + false + ); + return arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + thisValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + right + ) + ); + } + + static generatePathArg(monitorItem: string[] | undefined): arkts.ArrayExpression { + if (!monitorItem || monitorItem.length <= 0) { + return arkts.factory.createArrayExpression([]); + } + const params = monitorItem.map((itemName: string) => { + return factory.createMonitorPathsInfoParameter(itemName); + }); + return arkts.factory.createArrayExpression(params); + } + + static generateLambdaArg(originalName: string): arkts.ArrowFunctionExpression { + return arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + params: [UIFactory.createParameterDeclaration(MonitorNames.M_PARAM, MonitorNames.I_MONITOR)], + body: arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(originalName), undefined, [ + arkts.factory.createIdentifier(MonitorNames.M_PARAM), + ]) + ), + ]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ); + } + + static generateMonitorVariable(itemNameSplit: string[]): arkts.Expression { + const objectFirst: arkts.Expression = generateThisBacking(itemNameSplit[0]); + if (itemNameSplit.length === 1) { + return objectFirst; + } + itemNameSplit.shift(); + return this.recursiveCreateOptionalMember(objectFirst, itemNameSplit); + } + + /** + * recursively create member expression with node and property name in . + * + * @param typeAnnotation expecting property's original type annotation. + */ + static recursiveCreateOptionalMember(object: arkts.Expression, resNameArr: string[]): arkts.Expression { + if (resNameArr.length <= 0) { + return object; + } + const optionalInfo: OptionalMemberInfo = { isNumeric: false }; + if (isNumeric(resNameArr[0])) { + optionalInfo.isNumeric = true; + } + const newMember: arkts.Expression = this.createBlockStatementForOptionalExpression( + object, + resNameArr[0], + optionalInfo + ); + resNameArr.shift(); + return this.recursiveCreateOptionalMember(newMember, resNameArr); + } + + /** + * create IMonitorPathsInfo type parameter `{ path: "", lambda: () => { return this. } }`. + * + * @param monitorItem monitored property name. + */ + static createMonitorPathsInfoParameter(monitorItem: string): arkts.ObjectExpression { + const itemNameSplit: string[] = monitorItem.split('.'); + let monitorVariable: arkts.Expression = arkts.factory.createUndefinedLiteral(); + if (itemNameSplit.length > 0) { + monitorVariable = this.generateMonitorVariable(itemNameSplit); + } + return arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.factory.createProperty( + arkts.factory.createIdentifier(MonitorNames.PATH), + arkts.factory.create1StringLiteral(monitorItem) + ), + arkts.factory.createProperty( + arkts.factory.createIdentifier(MonitorNames.VALUE_CALL_CACK), + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + body: arkts.factory.createBlock([arkts.factory.createReturnStatement(monitorVariable)]), + returnTypeAnnotation: UIFactory.createTypeReferenceFromString(TypeNames.ANY), + }) + ) + ), + ], + false ); } } diff --git a/arkui-plugins/ui-plugins/property-translators/index.ts b/arkui-plugins/ui-plugins/property-translators/index.ts index 013eaa286d140d4664c074c678fa5e7f865be722..914006824c8ec1c4ffbcb63e1103f23147d53c3b 100644 --- a/arkui-plugins/ui-plugins/property-translators/index.ts +++ b/arkui-plugins/ui-plugins/property-translators/index.ts @@ -15,71 +15,272 @@ import * as arkts from '@koalaui/libarkts'; -import { PropertyTranslator } from './base'; -import { DecoratorNames, hasDecorator } from './utils'; -import { StateTranslator } from './state'; -import { PropTranslator } from './prop'; -import { StorageLinkTranslator } from './storagelink'; -import { LocalStorageLinkTranslator } from './localstoragelink'; -import { LinkTranslator } from './link'; -import { ObjectLinkTranslator } from './objectlink'; -import { LocalStoragePropTranslator } from './localstorageprop'; -import { regularPropertyTranslator } from './regularProperty'; -import { staticPropertyTranslator } from './staticProperty'; -import { isStatic } from '../utils'; -import { StoragePropTranslator } from './storageProp'; -import { ConsumeTranslator } from './consume'; -import { ProvideTranslator } from './provide'; -import { BuilderParamTranslator } from './builderParam'; +import { DecoratorNames } from '../../common/predefines'; +import { InterfacePropertyTranslator, MethodTranslator, PropertyTranslator } from './base'; +import { hasDecorator } from './utils'; +import { StateInterfaceTranslator, StateTranslator } from './state'; +import { PropInterfaceTranslator, PropTranslator } from './prop'; +import { StorageLinkInterfaceTranslator, StorageLinkTranslator } from './storagelink'; +import { LocalStorageLinkInterfaceTranslator, LocalStorageLinkTranslator } from './localstoragelink'; +import { LinkInterfaceTranslator, LinkTranslator } from './link'; +import { ObjectLinkInterfaceTranslator, ObjectLinkTranslator } from './objectlink'; +import { LocalStoragePropInterfaceTranslator, LocalStoragePropTranslator } from './localstorageprop'; +import { RegularInterfaceTranslator, RegularPropertyTranslator } from './regularProperty'; +import { StaticPropertyTranslator } from './staticProperty'; +import { CustomComponentInfo } from '../utils'; +import { StoragePropInterfaceTranslator, StoragePropTranslator } from './storageProp'; +import { ConsumeInterfaceTranslator, ConsumeTranslator } from './consume'; +import { ProvideInterfaceTranslator, ProvideTranslator } from './provide'; +import { BuilderParamInterfaceTranslator, BuilderParamTranslator } from './builderParam'; +import { PropRefInterfaceTranslator, PropRefTranslator } from './propRef'; import { ObservedTrackTranslator } from './observedTrack'; -import { ClassScopeInfo } from 'ui-plugins/checked-transformer'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { LocalInterfaceTranslator, LocalTranslator } from './local'; +import { StoragePropRefInterfaceTranslator, StoragePropRefTranslator } from './storagePropRef'; +import { LocalStoragePropRefInterfaceTranslator, LocalStoragePropRefTranslator } from './localStoragePropRef'; +import { ObservedV2TraceTranslator } from './observedV2Trace'; +import { ParamInterfaceTranslator, ParamTranslator } from './param'; +import { OnceInterfaceTranslator, OnceTranslator } from './once'; +import { ProviderInterfaceTranslator, ProviderTranslator } from './provider'; +import { ConsumerInterfaceTranslator, ConsumerTranslator } from './consumer'; +import { ComputedTranslator } from './computed'; +import { MonitorTranslator } from './monitor'; -export { PropertyTranslator }; +export { PropertyTranslator, InterfacePropertyTranslator }; +export type { ClassScopeInfo }; -export function classifyProperty(member: arkts.AstNode, structName: string): PropertyTranslator | undefined { - if (!arkts.isClassProperty(member)) return undefined; - if (isStatic(member)) return new staticPropertyTranslator(member, structName); +export function classifyStructMembers( + member: arkts.AstNode, + structInfo: CustomComponentInfo +): PropertyTranslator | MethodTranslator | undefined { + if (arkts.isClassProperty(member)) { + return classifyProperty(member, structInfo); + } else if (arkts.isMethodDefinition(member)) { + return classifyMethod(member, true, structInfo.name); + } + return undefined; +} + +export function classifyProperty( + property: arkts.AstNode, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (!arkts.isClassProperty(property)) return undefined; + if (StaticPropertyTranslator.canBeStaticTranslate(property)) { + return new StaticPropertyTranslator({ property, structInfo }); + } + let propertyTranslator: PropertyTranslator | undefined = undefined; + + propertyTranslator = classifyV1Property(property, structInfo); + if (!!propertyTranslator) { + return propertyTranslator; + } + + propertyTranslator = classifyV2Property(property, structInfo); + if (!!propertyTranslator) { + return propertyTranslator; + } - if (hasDecorator(member, DecoratorNames.STATE)) { - return new StateTranslator(member, structName); + return new RegularPropertyTranslator({ property, structInfo }); +} + +export function classifyV1Property( + property: arkts.ClassProperty, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (hasDecorator(property, DecoratorNames.STATE)) { + return new StateTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.STORAGE_LINK)) { + return new StorageLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_LINK)) { + return new LocalStorageLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LINK)) { + return new LinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.OBJECT_LINK)) { + return new ObjectLinkTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP)) { + return new LocalStoragePropTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return new LocalStoragePropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_LINK)) { - return new StorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_PROP)) { + return new StoragePropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_LINK)) { - return new LocalStorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_PROP_REF)) { + return new StoragePropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LINK)) { - return new LinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROP)) { + return new PropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.OBJECT_LINK)) { - return new ObjectLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROP_REF)) { + return new PropRefTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_PROP)) { - return new LocalStoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROVIDE)) { + return new ProvideTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_PROP)) { - return new StoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.CONSUME)) { + return new ConsumeTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROP)) { - return new PropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.BUILDER_PARAM)) { + return new BuilderParamTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROVIDE)) { - return new ProvideTranslator(member, structName); + + return undefined; +} + +export function classifyV2Property( + property: arkts.ClassProperty, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (hasDecorator(property, DecoratorNames.LOCAL)) { + return new LocalTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.ONCE)) { + return new OnceTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.CONSUME)) { - return new ConsumeTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PARAM)) { + return new ParamTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.PROVIDER)) { + return new ProviderTranslator({ property, structInfo }); + } + if (hasDecorator(property, DecoratorNames.CONSUMER)) { + return new ConsumerTranslator({ property, structInfo }); + } + + return undefined; +} + +export function classifyPropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + let interfacePropertyTranslater: InterfacePropertyTranslator | undefined = undefined; + + interfacePropertyTranslater = classifyV1PropertyInInterface(property); + if (!!interfacePropertyTranslater) { + return interfacePropertyTranslater; } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - return new BuilderParamTranslator(member, structName); + + interfacePropertyTranslater = classifyV2PropertyInInterface(property); + if (!!interfacePropertyTranslater) { + return interfacePropertyTranslater; } - return new regularPropertyTranslator(member, structName); + if (RegularInterfaceTranslator.canBeTranslated(property)) { + return new RegularInterfaceTranslator({ property }); + } + return undefined; } -export function classifyObservedTrack(member: arkts.AstNode, classScopeInfo: ClassScopeInfo): ObservedTrackTranslator | undefined { - if (!arkts.isClassProperty(member)) { +export function classifyV1PropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + if (StateInterfaceTranslator.canBeTranslated(property)) { + return new StateInterfaceTranslator({ property }); + } + if (LinkInterfaceTranslator.canBeTranslated(property)) { + return new LinkInterfaceTranslator({ property }); + } + if (PropInterfaceTranslator.canBeTranslated(property)) { + return new PropInterfaceTranslator({ property }); + } + if (PropRefInterfaceTranslator.canBeTranslated(property)) { + return new PropRefInterfaceTranslator({ property }); + } + if (ProvideInterfaceTranslator.canBeTranslated(property)) { + return new ProvideInterfaceTranslator({ property }); + } + if (ConsumeInterfaceTranslator.canBeTranslated(property)) { + return new ConsumeInterfaceTranslator({ property }); + } + if (StoragePropInterfaceTranslator.canBeTranslated(property)) { + return new StoragePropInterfaceTranslator({ property }); + } + if (StorageLinkInterfaceTranslator.canBeTranslated(property)) { + return new StorageLinkInterfaceTranslator({ property }); + } + if (StoragePropRefInterfaceTranslator.canBeTranslated(property)) { + return new StoragePropRefInterfaceTranslator({ property }); + } + if (LocalStoragePropRefInterfaceTranslator.canBeTranslated(property)) { + return new LocalStoragePropRefInterfaceTranslator({ property }); + } + if (BuilderParamInterfaceTranslator.canBeTranslated(property)) { + return new BuilderParamInterfaceTranslator({ property }); + } + if (LocalStoragePropInterfaceTranslator.canBeTranslated(property)) { + return new LocalStoragePropInterfaceTranslator({ property }); + } + if (LocalStorageLinkInterfaceTranslator.canBeTranslated(property)) { + return new LocalStorageLinkInterfaceTranslator({ property }); + } + if (ObjectLinkInterfaceTranslator.canBeTranslated(property)) { + return new ObjectLinkInterfaceTranslator({ property }); + } + return undefined; +} + +export function classifyV2PropertyInInterface(property: arkts.AstNode): InterfacePropertyTranslator | undefined { + if (LocalInterfaceTranslator.canBeTranslated(property)) { + return new LocalInterfaceTranslator({ property }); + } + if (OnceInterfaceTranslator.canBeTranslated(property)) { + return new OnceInterfaceTranslator({ property }); + } + if (ParamInterfaceTranslator.canBeTranslated(property)) { + return new ParamInterfaceTranslator({ property }); + } + if (ProviderInterfaceTranslator.canBeTranslated(property)) { + return new ProviderInterfaceTranslator({ property }); + } + if (ConsumerInterfaceTranslator.canBeTranslated(property)) { + return new ConsumerInterfaceTranslator({ property }); + } + return undefined; +} + +export type ObservedTranslator = ObservedV2TraceTranslator | ObservedTrackTranslator; + +export function classifyObservedClassProperty( + member: arkts.ClassProperty, + classScopeInfo: ClassScopeInfo +): ObservedTranslator | undefined { + if (classScopeInfo.isObservedV2) { + return new ObservedV2TraceTranslator(member, classScopeInfo); + } + if (classScopeInfo.isObserved || classScopeInfo.classHasTrack) { + return new ObservedTrackTranslator(member, classScopeInfo); + } + return undefined; +} + +export function classifyMethod( + member: arkts.AstNode, + isFromStruct: boolean, + className: string +): MethodTranslator | undefined { + if (!arkts.isMethodDefinition(member)) { return undefined; } - return new ObservedTrackTranslator(member, classScopeInfo); -} \ No newline at end of file + if (hasDecorator(member, DecoratorNames.COMPUTED)) { + return new ComputedTranslator(member, { isFromStruct, className }); + } + if (hasDecorator(member, DecoratorNames.MONITOR)) { + return new MonitorTranslator(member, { isFromStruct, className }); + } + return undefined; +} + +export function classifyInObservedClass( + member: arkts.AstNode, + classScopeInfo: ClassScopeInfo +): ObservedTranslator | MethodTranslator | undefined { + if (arkts.isClassProperty(member)) { + return classifyObservedClassProperty(member, classScopeInfo); + } else if (arkts.isMethodDefinition(member)) { + return classifyMethod(member, false, classScopeInfo.className); + } + return undefined; +} diff --git a/arkui-plugins/ui-plugins/property-translators/link.ts b/arkui-plugins/ui-plugins/property-translators/link.ts index eafd90c955bfc1232e20cb6549218f525d4642e3..35c50150f046a2ecbc597a6285e65285218d4fed 100644 --- a/arkui-plugins/ui-plugins/property-translators/link.ts +++ b/arkui-plugins/ui-plugins/property-translators/link.ts @@ -15,59 +15,67 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; import { factory } from './factory'; -import { createOptionalClassProperty } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; export class LinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string) { const test = factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), newName ); const args: arkts.Expression[] = [ arkts.factory.create1StringLiteral(originalName), arkts.factory.createTSNonNullExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', newName, false, true) + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + newName, + false, + true + ) ), ]; - judgeIfAddWatchFunc(args, this.property); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LINK_DECORATED); + collectStateManagementTypeImport(StateManagementTypes.LINK_SOURCE_TYPE); const consequent = arkts.BlockStatement.createBlockStatement([ arkts.factory.createExpressionStatement( arkts.factory.createAssignmentExpression( generateThisBacking(newName, false, false), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.createNewDecoratedInstantiate('LinkDecoratedVariable', this.property.typeAnnotation, args) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_LINK, this.propertyType, args, true) ) ), ]); @@ -76,27 +84,19 @@ export class LinkTranslator extends PropertyTranslator implements InitializerCon } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'LinkDecoratedVariable', + StateManagementTypes.LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -117,3 +117,45 @@ export class LinkTranslator extends PropertyTranslator implements InitializerCon return createSetter2(originalName, typeAnnotation, statement); } } + +export class LinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `DecoratedV1VariableBase | undefined`. + * + * @param method expecting getter with `@Link` and a setter with `@Link` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `DecoratedV1VariableBase | undefined`. + * + * @param property expecting property with `@Link`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LINK); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/local.ts b/arkui-plugins/ui-plugins/property-translators/local.ts new file mode 100644 index 0000000000000000000000000000000000000000..84a76401ef8c85ece2e07179945637ce804efdb1 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/local.ts @@ -0,0 +1,174 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { + InterfacePropertyTranslator, + InterfacePropertyTypes, + PropertyTranslator, + PropertyTranslatorOptions, +} from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { factory as UIFactory } from '../ui-factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class LocalTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + private isStatic: boolean; + + constructor(options: PropertyTranslatorOptions) { + super(options); + this.isStatic = this.property.isStatic; + } + + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + if (!this.isStatic) { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = this.createPropertyField(newName, originalName); + const thisValue: arkts.Expression = this.isStatic + ? UIFactory.generateMemberExpression(arkts.factory.createIdentifier(this.structInfo.name), newName) + : generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + createPropertyField(newName: string, originalName: string): arkts.ClassProperty { + return this.isStatic + ? arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.generateInitializeValue(originalName), + factory.createStageManagementType(StateManagementTypes.LOCAL_DECORATED, this.propertyType), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + false + ) + : factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue, false, this.isStatic); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement, this.isStatic); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + collectStateManagementTypeImport(StateManagementTypes.LOCAL_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + this.generateInitializeValue(originalName) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateInitializeValue(originalName: string): arkts.Expression { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + collectStateManagementTypeImport(StateManagementTypes.LOCAL_DECORATED); + return factory.generateStateMgmtFactoryCall( + this.isStatic ? StateManagementTypes.MAKE_STATIC_LOCAL : StateManagementTypes.MAKE_LOCAL, + this.propertyType, + args, + this.isStatic ? false : true + ); + } +} + +export class LocalInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ILocalDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Local` and a setter with `@Local` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ILocalDecoratedVariable | undefined`. + * + * @param property expecting property with `@Local`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts b/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f1909c4545e558475ee9469d520b6e45996d0ae --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/localStoragePropRef.ts @@ -0,0 +1,157 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class LocalStoragePropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const localStoragePropRefValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.LOCAL_STORAGE_PROP_REF + ); + if (!localStoragePropRefValueStr) { + throw new Error('LocalStoragePropRef required only one value!!'); + } + + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(localStoragePropRefValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED); + return arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_LOCAL_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class LocalStoragePropRefInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ILocalStoragePropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@LocalStoragePropRef` and a setter with `@LocalStoragePropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ILocalStoragePropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@LocalStoragePropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts index a050e985294f30dcc9ddc5b92c4bcc8673eb18da..da1c0787ecb3602fce78881d033521a9a3bbde0a 100755 --- a/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts @@ -16,93 +16,142 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { DecoratorNames, generateToRecord } from './utils'; - -function getLocalStorageLinkValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getLocalStorageLinkAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStorageLinkAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.LOCAL_STORAGE_LINK; - - if (isStorageLinkAnnotation && anno.properties.length === 1) { - return getLocalStorageLinkValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getLocalStorageLinkValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getLocalStorageLinkAnnotationValue(anno); - if (!!str) { - return str; - } - } - - return undefined; -} +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class LocalStorageLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const localStorageLinkValueStr: string | undefined = getLocalStorageLinkValueInAnnotation(this.property); + const localStorageLinkValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.LOCAL_STORAGE_LINK + ); if (!localStorageLinkValueStr) { - throw new Error('LocalStorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('LocalStorageLink required only one value!!'); } - const call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], - [ - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_entry_local_storage_'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createStringLiteral(localStorageLinkValueStr), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - ] - ); - + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(localStorageLinkValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - call + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_LOCAL_STORAGE_LINK, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class LocalStorageLinkInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `MutableState | undefined`. + * + * @param method expecting getter with `@LocalStorageLink` and a setter with `@LocalStorageLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `MutableState | undefined`. + * + * @param property expecting property with `@LocalStorageLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_LINK); } } diff --git a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts index d20d982cd7feb8f773a4cbf7ffb19e48cb4ab48d..7b5840e62f24d9e7e365605ac92fd2656b23ebfb 100644 --- a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts @@ -15,10 +15,13 @@ import * as arkts from '@koalaui/libarkts'; -import { DecoratorNames, generateToRecord } from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, StateManagementTypes } from '../../common/predefines'; +import { collectStateManagementTypeImport, generateToRecord, hasDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; function getLocalStorageporpValueStr(node: arkts.AstNode): string | undefined { if (!arkts.isClassProperty(node) || !node.value) return undefined; @@ -54,29 +57,55 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.SYNCED_PROPERTY, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + const member = arkts.factory.createTSNonNullExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + ); + const thisValue: arkts.MemberExpression = arkts.factory.createMemberExpression( + member, + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisValue); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisValue); + return [field, getter, setter]; } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); if (!localStorageporpValueStr) { - throw new Error('LocalStorageProp required only one value!!'); // TODO: replace this with proper error message. + throw new Error('LocalStorageProp required only one value!!'); } const insideMember = arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), @@ -86,17 +115,18 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false ); const binaryItem = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), + this.propertyType ? [this.propertyType] : [], [ insideMember, arkts.factory.createStringLiteral(localStorageporpValueStr), this.property.value ?? arkts.factory.createUndefinedLiteral(), ] ); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); const call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('propState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], + arkts.factory.createIdentifier(StateManagementTypes.PROP_STATE), + this.propertyType ? [this.propertyType] : [], [ arkts.factory.createMemberExpression( binaryItem, @@ -107,6 +137,7 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In ), ] ); + collectStateManagementTypeImport(StateManagementTypes.PROP_STATE); return arkts.factory.createAssignmentExpression( arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), @@ -123,31 +154,10 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); if (!localStorageporpValueStr) { - throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageLink required only one value!!'); } - - const StorageLinkStateValue = arkts.factory.createMemberExpression( - arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], - [ - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_entry_local_storage_'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createStringLiteral(localStorageporpValueStr), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - ] - ), - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); - + const StorageLinkStateValue = factory.createStorageLinkStateValue(this.property, localStorageporpValueStr); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); const test = arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), arkts.factory.createIdentifier(newName), @@ -155,7 +165,6 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false, false ); - const consequent = arkts.BlockStatement.createBlockStatement([ arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( @@ -171,7 +180,50 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In ) ), ]); - return arkts.factory.createExpressionStatement(arkts.factory.createIfStatement(test, consequent)); } } + +export class LocalStoragePropInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param method expecting getter with `@LocalStorageProp` and a setter with `@LocalStorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param property expecting property with `@LocalStorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/monitor.ts b/arkui-plugins/ui-plugins/property-translators/monitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..2070b4fd5f068b66d3ff3531e628fe548ce7c1f7 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/monitor.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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 { expectName } from '../../common/arkts-utils'; +import { StateManagementTypes } from '../../common/predefines'; +import { monitorField } from '../utils'; +import { collectStateManagementTypeImport, getValueInMonitorAnnotation } from './utils'; +import { MethodTranslator } from './base'; +import { InitializerConstructor } from './types'; +import { factory as UIFactory } from '../ui-factory'; +import { MonitorCache, MonitorInfo } from './cache/monitorCache'; + +export class MonitorTranslator extends MethodTranslator implements InitializerConstructor { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.method.name); + const newName: string = monitorField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const monitorItem: string[] | undefined = getValueInMonitorAnnotation( + this.method.scriptFunction.annotations + ); + const monitorPathsStr: string = !!monitorItem ? monitorItem.join(',') : ''; + const monitorInfo: MonitorInfo = { + monitorItem: monitorItem, + originalName: originalName, + newName: newName, + }; + MonitorCache.getInstance().collectMonitors(this.classInfo.className, monitorPathsStr, monitorInfo); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + collectStateManagementTypeImport(StateManagementTypes.MONITOR_DECORATED); + const field: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + undefined, + arkts.factory.createUnionType([ + UIFactory.createTypeReferenceFromString(StateManagementTypes.MONITOR_DECORATED), + arkts.factory.createETSUndefinedType(), + ]), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + + return [field]; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/objectlink.ts b/arkui-plugins/ui-plugins/property-translators/objectlink.ts index 8da1283a269f18d813221d7c34db35c7f9f96a26..0e5a4b48804878e95b5109abe655d66be0d95e35 100644 --- a/arkui-plugins/ui-plugins/property-translators/objectlink.ts +++ b/arkui-plugins/ui-plugins/property-translators/objectlink.ts @@ -15,121 +15,82 @@ import * as arkts from '@koalaui/libarkts'; -import { - createGetter, - DecoratorNames, - generateGetOrSetCall, - generateThisBacking, - generateToRecord, - hasDecorator, - judgeIfAddWatchFunc, -} from './utils'; -import { PropertyTranslator } from './base'; -import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { createGetter, generateGetOrSetCall, generateThisBacking, generateToRecord, hasDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; -import { createOptionalClassProperty } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; export class ObjectLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - if (!this.ifObservedDecoratedClass()) { - throw new Error('@ObjectLink decorated property only accepts @Observed decorated class instance'); // TODO: replace this with proper error message. - } - - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const initializers = arkts.factory.createTSNonNullExpression( + const initializers = arkts.factory.createTSAsExpression( factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), originalName - ) - ); - - const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('ObjectLinkDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) ), - args + this.propertyType, + false ); + const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers]; + factory.judgeIfAddWatchFunc(args, this.property); return arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_OBJECT_LINK, this.propertyType, args, true) ); } generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL - ); const member: arkts.MemberExpression = arkts.factory.createMemberExpression( generateThisBacking(newName, false, true), - arkts.factory.createIdentifier('update'), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ); const nonNullItem = arkts.factory.createTSNonNullExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true) - ); - return arkts.factory.createIfStatement( - binaryItem, - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(member, undefined, [nonNullItem]) - ), - ]) + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ) ); + return factory.createIfInUpdateStruct(originalName, member, [nonNullItem]); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ObjectLinkDecoratedVariable', + StateManagementTypes.OBJECT_LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); return [field, getter]; } @@ -140,14 +101,46 @@ export class ObjectLinkTranslator extends PropertyTranslator implements Initiali ): arkts.MethodDefinition { return createGetter(originalName, typeAnnotation, returnValue); } +} + +export class ObjectLinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } - ifObservedDecoratedClass(): boolean { - if (this.property.typeAnnotation && arkts.isETSTypeReference(this.property.typeAnnotation)) { - const decl = arkts.getDecl(this.property.typeAnnotation.part?.name!); - if (arkts.isClassDefinition(decl!) && hasDecorator(decl, DecoratorNames.OBSERVED)) { - return true; - } + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.OBJECT_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.OBJECT_LINK)) { + return true; } return false; } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ObjectLinkDecoratedVariable | undefined`. + * + * @param method expecting getter with `@ObjectLink` and a setter with `@ObjectLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.OBJECT_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ObjectLinkDecoratedVariable | undefined`. + * + * @param property expecting property with `@ObjectLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.OBJECT_LINK); + } } diff --git a/arkui-plugins/ui-plugins/property-translators/observedTrack.ts b/arkui-plugins/ui-plugins/property-translators/observedTrack.ts index 036132553c06bbb54e0c5a7cbd35848221a36eed..84cc3925701d68a52cc7fb698ff3f0c6fde5549d 100644 --- a/arkui-plugins/ui-plugins/property-translators/observedTrack.ts +++ b/arkui-plugins/ui-plugins/property-translators/observedTrack.ts @@ -14,141 +14,144 @@ */ import * as arkts from '@koalaui/libarkts'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { DecoratorNames, hasDecorator } from './utils'; -import { ClassScopeInfo } from 'ui-plugins/checked-transformer'; - -export class ObservedTrackTranslator { - constructor(protected property: arkts.ClassProperty, protected classScopeInfo: ClassScopeInfo) {} - - private hasImplement: boolean = expectName(this.property.key).startsWith(''); - private isTracked: boolean = hasDecorator(this.property, DecoratorNames.TRACK); +import { annotation, backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, StateManagementTypes, ObservedNames } from '../../common/predefines'; +import { ObservedPropertyTranslator } from './base'; +import { + collectStateManagementTypeImport, + generateThisBacking, + hasDecorator, + hasDecoratorName, + removeDecorator, + removeImplementProperty, +} from './utils'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { factory } from './factory'; +import { factory as uiFactory } from '../ui-factory'; + +export class ObservedTrackTranslator extends ObservedPropertyTranslator { + private hasImplement: boolean; + private isTracked: boolean; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + super(property, classScopeInfo); + this.hasImplement = expectName(this.property.key).startsWith(ObservedNames.PROPERTY_PREFIX); + this.isTracked = hasDecorator(this.property, DecoratorNames.TRACK); + } translateMember(): arkts.AstNode[] { if (!this.isTracked && (this.classScopeInfo.classHasTrack || !this.classScopeInfo.isObserved)) { return [this.property]; } const originalName: string = this.hasImplement - ? this.removeImplementProperty(expectName(this.property.key)) + ? removeImplementProperty(expectName(this.property.key)) : expectName(this.property.key); const newName: string = backingField(originalName); - let properyIsClass = false; - - if (this.property.typeAnnotation && arkts.isETSTypeReference(this.property.typeAnnotation)) { - const decl = arkts.getDecl(this.property.typeAnnotation.part?.name!); - if (arkts.isClassDefinition(decl!)) { - properyIsClass = true; - } - } - const field = this.createField(originalName, newName, properyIsClass); - - this.transformGetterSetter(originalName, newName, properyIsClass); - + const field = this.createField(originalName, newName); + this.transformGetterSetter(originalName, newName); return [...field]; } - createField(originalName: string, newName: string, properyIsClass: boolean): arkts.ClassProperty[] { - const backingField = properyIsClass - ? this.propertyIsClassField(newName) - : arkts.factory.createClassProperty( - arkts.factory.createIdentifier(newName), - this.property.value, - this.property.typeAnnotation, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); + createField(originalName: string, newName: string): arkts.ClassProperty[] { + const backingField = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.property.value, + this.propertyType, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + if (!this.property.value) { + backingField.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + } + const annotations: arkts.AnnotationUsage[] = [...this.property.annotations]; + if ( + !hasDecoratorName(this.property, DecoratorNames.JSONSTRINGIFYIGNORE) && + !hasDecoratorName(this.property, DecoratorNames.JSONRENAME) + ) { + annotations.push( + annotation(DecoratorNames.JSONRENAME).addProperty( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.NEW_NAME), + arkts.factory.createStringLiteral(originalName), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + false + ) + ) + ); + } + backingField.setAnnotations(annotations); + removeDecorator(backingField, DecoratorNames.TRACK); if (!this.isTracked) { return [backingField]; } const metaField = this.metaField(originalName); + metaField.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); return [backingField, metaField]; } - createGetter(originalName: string, newName: string, properyIsClass: boolean): arkts.MethodDefinition { - const ifRefDepth: arkts.IfStatement = this.getterIfRefDepth(originalName); - const returnMember: arkts.ReturnStatement = this.getterReturnMember(properyIsClass, newName); - const setObservationDepth = this.getterSetObservationDepth(newName); - - const body = arkts.factory.createBlock([ - ifRefDepth, - ...(properyIsClass ? [setObservationDepth] : []), - returnMember, - ]); - - const scriptFunction = arkts.factory.createScriptFunction( - body, - arkts.FunctionSignature.createFunctionSignature(undefined, [], this.property.typeAnnotation, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, - arkts.factory.createIdentifier(originalName), - scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false + createGetter(originalName: string, newName: string): arkts.MethodDefinition { + const conditionalAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.CONDITIONAL_ADD_REF), undefined, [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + this.metaIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ]) ); + const backingMember: arkts.Expression = generateThisBacking(newName); + const returnMember: arkts.ReturnStatement = arkts.factory.createReturnStatement( + this.property.value + ? backingMember + : arkts.factory.createTSAsExpression(backingMember, this.propertyType, false) + ); + const body = arkts.factory.createBlock([conditionalAddRef, returnMember]); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + returnTypeAnnotation: this.propertyType, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); } - createSetter(originalName: string, newName: string, properyIsClass: boolean): arkts.MethodDefinition { - const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(properyIsClass, originalName, newName); + createSetter(originalName: string, newName: string): arkts.MethodDefinition { + const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(originalName, newName); const body = arkts.factory.createBlock([ifEqualsNewValue]); const param = arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier('newValue', this.property.typeAnnotation), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE, this.propertyType), undefined ); - const scriptFunction = arkts.factory.createScriptFunction( - body, - arkts.FunctionSignature.createFunctionSignature(undefined, [param], undefined, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, - arkts.factory.createIdentifier(originalName), - scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, - false - ); - } - - genThisBacking(newName: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); - } - - genThisBackingValue(newName: string): arkts.MemberExpression { - return arkts.factory.createMemberExpression( - this.genThisBacking(newName), - arkts.factory.createIdentifier('value'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + params: [param], + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, + }, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }); } metaIdentifier(originalName: string): arkts.Identifier { return this.isTracked - ? arkts.factory.createIdentifier(`__meta_${originalName}`) - : arkts.factory.createIdentifier('__meta'); - } - - removeImplementProperty(originalName: string): string { - const prefix = ''; - return originalName.substring(prefix.length); + ? arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`) + : arkts.factory.createIdentifier(StateManagementTypes.META); } - transformGetterSetter(originalName: string, newName: string, properyIsClass: boolean): void { - const newGetter = this.createGetter(originalName, newName, properyIsClass); - const newSetter = this.createSetter(originalName, newName, properyIsClass); + transformGetterSetter(originalName: string, newName: string): void { + const newGetter = this.createGetter(originalName, newName); + const newSetter = this.createSetter(originalName, newName); if (this.hasImplement) { { const idx: number = this.classScopeInfo.getters.findIndex( @@ -181,124 +184,25 @@ export class ObservedTrackTranslator { } } - propertyIsClassField(newName: string): arkts.ClassProperty { - return arkts.factory.createClassProperty( - arkts.factory.createIdentifier(newName), - this.property.value - ? arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('BackingValue'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - [this.property.value] - ) - : undefined, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('BackingValue'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, - false - ); - } - metaField(originalName: string): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); return arkts.factory.createClassProperty( - arkts.factory.createIdentifier(`__meta_${originalName}`), - arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), - [arkts.factory.createStringLiteral('@Track')] - ), - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('MutableStateMeta')) - ), + arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + uiFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, false ); } - getterIfRefDepth(originalName: string): arkts.IfStatement { - return arkts.factory.createIfStatement( - arkts.factory.createBinaryExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createNumericLiteral(0), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_GREATER_THAN - ), - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - this.metaIdentifier(originalName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createIdentifier('addRef'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined, - false, - false - ) - ), - ]) - ); - } - - getterSetObservationDepth(newName: string): arkts.ExpressionStatement { - return arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(arkts.factory.createIdentifier('setObservationDepth'), undefined, [ - this.genThisBackingValue(newName), - arkts.factory.createBinaryExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('_permissibleAddRefDepth'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - arkts.factory.createNumericLiteral(1), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_MINUS - ), - ]) - ); - } - - getterReturnMember(properyIsClass: boolean, newName: string): arkts.ReturnStatement { - return arkts.factory.createReturnStatement( - properyIsClass ? this.genThisBackingValue(newName) : this.genThisBacking(newName) - ); - } - - setterIfEqualsNewValue(properyIsClass: boolean, originalName: string, newName: string): arkts.IfStatement { - const backingValue = properyIsClass ? this.genThisBackingValue(newName) : this.genThisBacking(newName); + setterIfEqualsNewValue(originalName: string, newName: string): arkts.IfStatement { + const backingValue = generateThisBacking(newName); const setNewValue = arkts.factory.createExpressionStatement( arkts.factory.createAssignmentExpression( backingValue, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('newValue') + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE) ) ); @@ -312,7 +216,7 @@ export class ObservedTrackTranslator { false, false ), - arkts.factory.createIdentifier('fireChange'), + arkts.factory.createIdentifier(ObservedNames.FIRE_CHANGE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false @@ -323,23 +227,15 @@ export class ObservedTrackTranslator { ); const subscribingWatches = arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier('executeOnSubscribingWatches'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [arkts.factory.createStringLiteral(originalName)] - ) + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.EXECUATE_WATCHES), undefined, [ + arkts.factory.createStringLiteral(originalName), + ]) ); return arkts.factory.createIfStatement( arkts.factory.createBinaryExpression( backingValue, - arkts.factory.createIdentifier('newValue'), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL ), arkts.factory.createBlock([setNewValue, fireChange, subscribingWatches]) diff --git a/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts b/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts new file mode 100644 index 0000000000000000000000000000000000000000..673184f74318ddd20eef530decf912b092bef47b --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/observedV2Trace.ts @@ -0,0 +1,274 @@ +/* + * Copyright (c) 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 { annotation, backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, ObservedNames, StateManagementTypes } from '../../common/predefines'; +import { ObservedPropertyTranslator } from './base'; +import { + collectStateManagementTypeImport, + generateThisBacking, + hasDecorator, + hasDecoratorName, + removeDecorator, + removeImplementProperty, +} from './utils'; +import { ClassScopeInfo } from '../struct-translators/utils'; +import { factory } from './factory'; +import { factory as uiFactory } from '../ui-factory'; +import { ImportCollector } from '../../common/import-collector'; + +export class ObservedV2TraceTranslator extends ObservedPropertyTranslator { + private hasImplement: boolean; + private isTraced: boolean; + private isStatic: boolean; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + super(property, classScopeInfo); + this.hasImplement = expectName(this.property.key).startsWith(ObservedNames.PROPERTY_PREFIX); + this.isTraced = hasDecorator(this.property, DecoratorNames.TRACE); + this.isStatic = arkts.hasModifierFlag(this.property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC); + } + + translateMember(): arkts.AstNode[] { + if (!this.isTraced) { + return [this.property]; + } + const originalName: string = this.hasImplement + ? removeImplementProperty(expectName(this.property.key)) + : expectName(this.property.key); + const newName: string = backingField(originalName); + const field = this.createField(originalName, newName); + this.transformGetterSetter(originalName, newName); + return [...field]; + } + + createField(originalName: string, newName: string): arkts.ClassProperty[] { + const backingField = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName), + this.property.value, + this.propertyType, + this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + if (!this.property.value) { + backingField.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + } + const annotations: arkts.AnnotationUsage[] = [...this.property.annotations]; + if ( + !hasDecoratorName(this.property, DecoratorNames.JSONSTRINGIFYIGNORE) && + !hasDecoratorName(this.property, DecoratorNames.JSONRENAME) + ) { + annotations.push( + annotation(DecoratorNames.JSONRENAME).addProperty( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(ObservedNames.NEW_NAME), + arkts.factory.createStringLiteral(originalName), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + false + ) + ) + ); + } + backingField.setAnnotations(annotations); + removeDecorator(backingField, DecoratorNames.TRACE); + const metaField = this.metaField(originalName); + metaField.setAnnotations([annotation(DecoratorNames.JSONSTRINGIFYIGNORE)]); + return [backingField, metaField]; + } + + createGetter( + originalName: string, + newName: string, + methodModifier: arkts.Es2pandaModifierFlags + ): arkts.MethodDefinition { + const classIdent: arkts.Identifier = arkts.factory.createIdentifier(this.classScopeInfo.className); + ImportCollector.getInstance().collectImport(StateManagementTypes.UI_UTILS); + const observedMember: arkts.Expression = this.isStatic + ? uiFactory.generateMemberExpression(classIdent.clone(), newName) + : generateThisBacking(newName); + const returnMember: arkts.ReturnStatement = arkts.factory.createReturnStatement( + arkts.factory.createCallExpression( + uiFactory.generateMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.UI_UTILS), + StateManagementTypes.MAKE_OBSERVED + ), + undefined, + [ + this.property.value + ? observedMember + : arkts.factory.createTSAsExpression(observedMember, this.propertyType, false), + ] + ) + ); + const body = arkts.factory.createBlock([this.createAddRef(originalName, classIdent), returnMember]); + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + returnTypeAnnotation: this.propertyType, + modifiers: methodModifier, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + }, + modifiers: methodModifier, + }); + } + + createSetter( + originalName: string, + newName: string, + methodModifier: arkts.Es2pandaModifierFlags + ): arkts.MethodDefinition { + const ifEqualsNewValue: arkts.IfStatement = this.setterIfEqualsNewValue(originalName, newName); + const body = arkts.factory.createBlock([ifEqualsNewValue]); + const param = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE, this.propertyType), + undefined + ); + + return uiFactory.createMethodDefinition({ + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, + key: arkts.factory.createIdentifier(originalName), + function: { + body: body, + params: [param], + modifiers: methodModifier, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, + }, + modifiers: methodModifier, + }); + } + + transformGetterSetter(originalName: string, newName: string): void { + const methodModifier = this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + const newGetter = this.createGetter(originalName, newName, methodModifier); + const newSetter = this.createSetter(originalName, newName, methodModifier); + if (this.hasImplement) { + { + const idx: number = this.classScopeInfo.getters.findIndex( + (getter) => getter.name.name === originalName + ); + const originGetter: arkts.MethodDefinition = this.classScopeInfo.getters[idx]; + const originSetter: arkts.MethodDefinition = originGetter.overloads[0]; + const updateGetter: arkts.MethodDefinition = arkts.factory.updateMethodDefinition( + originGetter, + originGetter.kind, + newGetter.name, + newGetter.scriptFunction.addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD), + originGetter.modifiers, + false + ); + arkts.factory.updateMethodDefinition( + originSetter, + originSetter.kind, + newSetter.name, + newSetter.scriptFunction + .addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_OVERLOAD) + .addFlag(arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD), + originSetter.modifiers, + false + ); + this.classScopeInfo.getters[idx] = updateGetter; + } + } else { + this.classScopeInfo.getters.push(...[newGetter, newSetter]); + } + } + + metaField(originalName: string): arkts.ClassProperty { + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + return arkts.factory.createClassProperty( + arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`), + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_MUTABLESTATE_META, undefined, [], false), + uiFactory.createTypeReferenceFromString(StateManagementTypes.MUTABLE_STATE_META), + this.isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + } + + createAddRef(originalName: string, classIdent: arkts.Identifier): arkts.ExpressionStatement { + const metaName: string = `${StateManagementTypes.META}_${originalName}`; + const conditionalAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.CONDITIONAL_ADD_REF), undefined, [ + generateThisBacking(metaName), + ]) + ); + const metaAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + uiFactory.generateMemberExpression( + uiFactory.generateMemberExpression(classIdent.clone(), metaName), + ObservedNames.ADD_REF + ), + undefined, + undefined + ) + ); + return this.isStatic ? metaAddRef : conditionalAddRef; + } + + setterIfEqualsNewValue(originalName: string, newName: string): arkts.IfStatement { + const classIdent: arkts.Identifier = arkts.factory.createIdentifier(this.classScopeInfo.className); + const metaName: string = `${StateManagementTypes.META}_${originalName}`; + const backingValue: arkts.Expression = generateThisBacking(newName); + const metaValue: arkts.Expression = generateThisBacking(metaName); + const staticMetaValue: arkts.Expression = uiFactory.generateMemberExpression(classIdent.clone(), metaName); + const staticBackingValue: arkts.Expression = uiFactory.generateMemberExpression(classIdent.clone(), newName); + const setNewValue = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + this.isStatic ? staticBackingValue : backingValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE) + ) + ); + + const fireChange = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + this.isStatic ? staticMetaValue.clone() : metaValue.clone(), + arkts.factory.createIdentifier(ObservedNames.FIRE_CHANGE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); + + const subscribingWatches = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression(generateThisBacking(ObservedNames.EXECUATE_WATCHES), undefined, [ + arkts.factory.createStringLiteral(originalName), + ]) + ); + + const consequentArr = this.isStatic ? [setNewValue, fireChange] : [setNewValue, fireChange, subscribingWatches]; + return arkts.factory.createIfStatement( + arkts.factory.createBinaryExpression( + this.isStatic ? staticBackingValue.clone() : backingValue.clone(), + arkts.factory.createIdentifier(ObservedNames.NEW_VALUE), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL + ), + arkts.factory.createBlock(consequentArr) + ); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/once.ts b/arkui-plugins/ui-plugins/property-translators/once.ts new file mode 100644 index 0000000000000000000000000000000000000000..df0468990e8c9782fd8635a6cc10fd423a21783d --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/once.ts @@ -0,0 +1,140 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class OnceTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.ONCE_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const outInitialize: arkts.Expression = factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + collectStateManagementTypeImport(StateManagementTypes.ONCE_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PARAM_ONCE, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class OnceInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.ONCE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.ONCE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IParamOnceDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Once` and a setter with `@Once` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.ONCE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IParamOnceDecoratedVariable | undefined`. + * + * @param property expecting property with `@Once`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.ONCE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/param.ts b/arkui-plugins/ui-plugins/property-translators/param.ts new file mode 100644 index 0000000000000000000000000000000000000000..95caf467a11ca75b30bf0f353c1254db96b43643 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/param.ts @@ -0,0 +1,147 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + createGetter, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + collectStateManagementTypeImport, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(generateThisBacking(newName), originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PARAM_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + + return [field, getter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + collectStateManagementTypeImport(StateManagementTypes.PARAM_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PARAM, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(mutableThis), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + this.propertyType, + false + ), + ]); + } +} + +export class ParamInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PARAM)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PARAM)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IParamDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Param` and a setter with `@Param` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PARAM); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IParamDecoratedVariable | undefined`. + * + * @param property expecting property with `@Param`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PARAM); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/prop.ts b/arkui-plugins/ui-plugins/property-translators/prop.ts index b8bb3d9914cf0e37a87249cf2d94c899f0caab9c..f4e6fbcb1038f592abc98f3a3f301b488a9451d4 100644 --- a/arkui-plugins/ui-plugins/property-translators/prop.ts +++ b/arkui-plugins/ui-plugins/property-translators/prop.ts @@ -15,65 +15,58 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { - generateToRecord, + collectStateManagementTypeImport, createGetter, createSetter2, generateGetOrSetCall, generateThisBacking, - judgeIfAddWatchFunc, + generateToRecord, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class PropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const mutableThis: arkts.Expression = generateThisBacking(newName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - currentStructInfo.updateBody.push(updateStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'PropDecoratedVariable', + StateManagementTypes.PROP_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -95,73 +88,81 @@ export class PropTranslator extends PropertyTranslator implements InitializerCon } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING - ); const args: arkts.Expression[] = [ arkts.factory.create1StringLiteral(originalName), - this.property.value - ? binaryItem - : arkts.factory.createTSAsExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true), - this.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, - false - ), + factory.generateInitializeValue(this.property, this.propertyType, originalName), ]; - judgeIfAddWatchFunc(args, this.property); - const right = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('PropDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.PROP_DECORATED); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROP, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL - ); const member: arkts.MemberExpression = arkts.factory.createMemberExpression( arkts.factory.createTSNonNullExpression(mutableThis), - arkts.factory.createIdentifier('update'), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ); - return arkts.factory.createIfStatement( - binaryItem, - arkts.factory.createBlock([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression(member, undefined, [ - arkts.factory.createTSAsExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true), - this.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, - false - ), - ]) + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true ), - ]) - ); + this.propertyType, + false + ), + ]); + } +} + +export class PropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Prop` and a setter with `@Prop` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param property expecting property with `@Prop`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROP); } } diff --git a/arkui-plugins/ui-plugins/property-translators/propRef.ts b/arkui-plugins/ui-plugins/property-translators/propRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..750b1a5d9e3bb559e8b93ffb9172659b895f0a96 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/propRef.ts @@ -0,0 +1,168 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + generateToRecord, + createGetter, + createSetter2, + generateGetOrSetCall, + generateThisBacking, + collectStateManagementTypeImport, + hasDecorator, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class PropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const mutableThis: arkts.Expression = generateThisBacking(newName); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.PROP_REF_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROP_REF, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(mutableThis), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + this.propertyType, + false + ), + ]); + } +} + +export class PropRefInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IPropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@PropRef` and a setter with `@PropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IPropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@PropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/provide.ts b/arkui-plugins/ui-plugins/property-translators/provide.ts index 144d1239a1d34d35c53518940cb2398ade5fd8cc..84a84a4f7c2a08310d7abde697317dd8673f4307 100644 --- a/arkui-plugins/ui-plugins/property-translators/provide.ts +++ b/arkui-plugins/ui-plugins/property-translators/provide.ts @@ -15,62 +15,55 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { - generateToRecord, createGetter, + generateToRecord, createSetter2, generateThisBacking, generateGetOrSetCall, getValueInProvideAnnotation, ProvideOptions, + hasDecorator, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class ProvideTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'ProvideDecoratedVariable', + StateManagementTypes.PROVIDE_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -95,11 +88,60 @@ export class ProvideTranslator extends PropertyTranslator implements Initializer const options: undefined | ProvideOptions = getValueInProvideAnnotation(this.property); const alias: string = options?.alias ?? originalName; const allowOverride: boolean = options?.allowOverride ?? false; + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral(alias), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + arkts.factory.createBooleanLiteral(allowOverride), + ]; + factory.judgeIfAddWatchFunc(args, this.property); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.generateAddProvideVarCall(originalName, this.property, alias, allowOverride) + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROVIDE, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class ProvideInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROVIDE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROVIDE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `ProvideDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Provide` and a setter with `@Provide` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROVIDE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `ProvideDecoratedVariable | undefined`. + * + * @param property expecting property with `@Provide`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROVIDE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/provider.ts b/arkui-plugins/ui-plugins/property-translators/provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..1b70ff77dc188461c7e46f6cecd9aaa7585a0571 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/provider.ts @@ -0,0 +1,137 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class ProviderTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PROVIDER_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + arkts.factory.create1StringLiteral( + getValueInAnnotation(this.property, DecoratorNames.PROVIDER) ?? originalName + ), + this.property.value!, + ]; + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROVIDER, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } +} + +export class ProviderInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROVIDER)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROVIDER)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IProviderDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Provider` and a setter with `@Provider` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROVIDER); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IProviderDecoratedVariable | undefined`. + * + * @param property expecting property with `@Provider`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROVIDER); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/regularProperty.ts b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts index d89b471e77471008589650dea558d5d86e5e201c..9cae2857dabee1cf4823c2c1bc143eaa6e1cc497 100644 --- a/arkui-plugins/ui-plugins/property-translators/regularProperty.ts +++ b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts @@ -15,37 +15,44 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, generateToRecord, generateThisBacking, createSetter2 } from './utils'; -import { PropertyTranslator } from './base'; +import { createGetter, generateToRecord, generateThisBacking, createSetter2, isCustomDialogController } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { createOptionalClassProperty } from '../utils'; import { backingField, expectName } from '../../common/arkts-utils'; import { factory } from './factory'; +import { CustomComponentNames } from '../utils'; +import { PropertyCache } from './cache/propertyCache'; -export class regularPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { +export class RegularPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); - const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + const value = this.property.value ?? arkts.factory.createUndefinedLiteral(); + let initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName, value); + if ( + !!this.propertyType && + !!this.structInfo.annotations.customdialog && + isCustomDialogController(this.propertyType) + ) { + initializeStruct = this.generateControllerInit(originalName, initializeStruct); + } + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - '', + undefined, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, false); @@ -53,19 +60,15 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini arkts.factory.createAssignmentExpression( thisValue, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - arkts.factory.createIdentifier('value'), + arkts.factory.createIdentifier('value') ) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, - this.property.typeAnnotation, - arkts.factory.createTSAsExpression(thisValue, this.property.typeAnnotation, false) - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + this.propertyType, + arkts.factory.createTSAsExpression(thisValue, this.propertyType, false) ); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -86,13 +89,13 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini return createSetter2(originalName, typeAnnotation, statement); } - generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + generateInitializeStruct(newName: string, originalName: string, value: arkts.Expression): arkts.AstNode { const binaryItem = arkts.factory.createBinaryExpression( factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), originalName ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), + value ?? arkts.factory.createUndefinedLiteral(), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( @@ -102,4 +105,24 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini ); return arkts.factory.createExpressionStatement(assign); } + + generateControllerInit(originalName: string, initializeStruct: arkts.AstNode): arkts.AstNode { + return arkts.factory.createIfStatement( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ), + arkts.factory.createBlock([initializeStruct]) + ); + } +} + +export class RegularInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + return true; + } } diff --git a/arkui-plugins/ui-plugins/property-translators/return-transformer.ts b/arkui-plugins/ui-plugins/property-translators/return-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..bdc1a536e91dca94b0080d07ccc8068f7e148b92 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/return-transformer.ts @@ -0,0 +1,42 @@ +/* + * 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 } from '../../common/abstract-visitor'; + +export class ReturnTransformer extends AbstractVisitor { + private _types: arkts.TypeNode[] = []; + + reset(): void { + super.reset(); + this._types = []; + } + + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.isReturnStatement(node) && node.argument) { + const type = arkts.createTypeNodeFromTsType(node.argument); + if (!!type && arkts.isTypeNode(type)) { + this._types.push(type); + } + return node; + } + return node; + } + + get types(): arkts.TypeNode[] { + return this._types; + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/state.ts b/arkui-plugins/ui-plugins/property-translators/state.ts index 667fabac74a82ac38516770170ca9f536560aa3c..20df22709c73a8de83689a51bad060c8368cc84c 100644 --- a/arkui-plugins/ui-plugins/property-translators/state.ts +++ b/arkui-plugins/ui-plugins/property-translators/state.ts @@ -15,61 +15,54 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; import { generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + hasDecorator, + collectStateManagementTypeImport, } from './utils'; -import { PropertyTranslator } from './base'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { backingField, expectName } from '../../common/arkts-utils'; -import { createOptionalClassProperty } from '../utils'; import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StateTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field: arkts.ClassProperty = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'StateDecoratedVariable', + StateManagementTypes.STATE_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -91,32 +84,59 @@ export class StateTranslator extends PropertyTranslator implements InitializerCo } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const binaryItem = arkts.factory.createBinaryExpression( - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - this.property.value ?? arkts.factory.createUndefinedLiteral(), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING - ); - const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), binaryItem]; - judgeIfAddWatchFunc(args, this.property); - const right = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StateDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STATE_DECORATED); const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_STATE, this.propertyType, args, true) ); return arkts.factory.createExpressionStatement(assign); } } + +export class StateInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STATE)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STATE)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StateDecoratedVariable | undefined`. + * + * @param method expecting getter with `@State` and a setter with `@State` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STATE); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StateDecoratedVariable | undefined`. + * + * @param property expecting property with `@State`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STATE); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/staticProperty.ts b/arkui-plugins/ui-plugins/property-translators/staticProperty.ts index 12e9535c5eebcbfcafca53beb45df1dcc221a1b0..c36c9781adf7de7d64955654f23554e7a1926b5a 100644 --- a/arkui-plugins/ui-plugins/property-translators/staticProperty.ts +++ b/arkui-plugins/ui-plugins/property-translators/staticProperty.ts @@ -15,12 +15,14 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter } from './utils'; +import { createGetter, createSetter, hasDecorator } from './utils'; import { PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { isStatic } from '../../ui-plugins/utils'; +import { DecoratorNames } from '../../common/predefines'; -export class staticPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { +export class StaticPropertyTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); @@ -49,4 +51,8 @@ export class staticPropertyTranslator extends PropertyTranslator implements Init const right: arkts.Identifier = arkts.factory.createIdentifier('value'); return createSetter(originalName, typeAnnotation, left, right); } + + static canBeStaticTranslate(node: arkts.ClassProperty): boolean { + return isStatic(node) && !hasDecorator(node, DecoratorNames.LOCAL); + } } diff --git a/arkui-plugins/ui-plugins/property-translators/storageProp.ts b/arkui-plugins/ui-plugins/property-translators/storageProp.ts index 2693f749d866c47d3e6651bcb961979954473f58..e4b0978d2f40629895d1f76d9d88aae656a74086 100644 --- a/arkui-plugins/ui-plugins/property-translators/storageProp.ts +++ b/arkui-plugins/ui-plugins/property-translators/storageProp.ts @@ -16,74 +16,47 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { - DecoratorNames, generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, } from './utils'; -import { createOptionalClassProperty } from '../utils'; - -function getStoragePropValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getStoragePropAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStoragePropAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.STORAGE_PROP; - - if (isStoragePropAnnotation && anno.properties.length === 1) { - return getStoragePropValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getStoragePropValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getStoragePropAnnotationValue(anno); - if (!!str) { - return str; - } - } - return undefined; -} +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StoragePropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const storagePropValueStr: string | undefined = getStoragePropValueInAnnotation(this.property); + const storagePropValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_PROP + ); if (!storagePropValueStr) { - throw new Error('StorageProp required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageProp required only one value!!'); } const args: arkts.Expression[] = [ @@ -91,55 +64,35 @@ export class StoragePropTranslator extends PropertyTranslator implements Initial arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), ]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StoragePropDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_PROP_REF_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) ); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( + const field = factory.createOptionalClassProperty( newName, this.property, - 'StoragePropDecoratedVariable', + StateManagementTypes.STORAGE_PROP_REF_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -159,3 +112,45 @@ export class StoragePropTranslator extends PropertyTranslator implements Initial return createSetter2(originalName, typeAnnotation, statement); } } + +export class StoragePropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StorageProp` and a setter with `@StorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param property expecting property with `@StorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts b/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..a27aa4be09f793d6b1fbf656e93da672cfd23918 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/storagePropRef.ts @@ -0,0 +1,158 @@ +/* + * Copyright (c) 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 { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class StoragePropRefTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const storagePropValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_PROP_REF + ); + if (!storagePropValueStr) { + throw new Error('StoragePropRef required only one value!!'); + } + + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(storagePropValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_PROP_REF_DECORATED); + + return arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.STORAGE_PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class StoragePropRefInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP_REF)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP_REF)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `IStoragePropRefDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StoragePropRef` and a setter with `@StoragePropRef` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_PROP_REF); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `IStoragePropRefDecoratedVariable | undefined`. + * + * @param property expecting property with `@StoragePropRef`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_PROP_REF); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/storagelink.ts b/arkui-plugins/ui-plugins/property-translators/storagelink.ts index efd83e9d60c3adc50a715613808566a85b108ef7..e90016c3c3650dfef416ca4d4aefb46446b5c6a1 100644 --- a/arkui-plugins/ui-plugins/property-translators/storagelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/storagelink.ts @@ -16,74 +16,47 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; -import { PropertyTranslator } from './base'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { - DecoratorNames, generateToRecord, createGetter, createSetter2, generateThisBacking, generateGetOrSetCall, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, } from './utils'; -import { createOptionalClassProperty } from '../utils'; - -function getStorageLinkValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) return undefined; - - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; -} - -function getStorageLinkAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isStorageLinkAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.STORAGE_LINK; - - if (isStorageLinkAnnotation && anno.properties.length === 1) { - return getStorageLinkValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getStorageLinkValueInAnnotation(node: arkts.ClassProperty): string | undefined { - const annotations: readonly arkts.AnnotationUsage[] = node.annotations; - - for (let i = 0; i < annotations.length; i++) { - const anno: arkts.AnnotationUsage = annotations[i]; - const str: string | undefined = getStorageLinkAnnotationValue(anno); - if (!!str) { - return str; - } - } - return undefined; -} +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; export class StorageLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { const originalName: string = expectName(this.property.key); const newName: string = backingField(originalName); - this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... + this.cacheTranslatedInitializer(newName, originalName); return this.translateWithoutInitializer(newName, originalName); } cacheTranslatedInitializer(newName: string, originalName: string): void { - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - - if (currentStructInfo.isReusable) { + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { const toRecord = generateToRecord(newName, originalName); - currentStructInfo.toRecordBody.push(toRecord); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); } - - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); } generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - const storageLinkValueStr: string | undefined = getStorageLinkValueInAnnotation(this.property); + const storageLinkValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_LINK + ); if (!storageLinkValueStr) { - throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. + throw new Error('StorageLink required only one value!!'); } const args: arkts.Expression[] = [ @@ -91,55 +64,29 @@ export class StorageLinkTranslator extends PropertyTranslator implements Initial arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), ]; - judgeIfAddWatchFunc(args, this.property); - - const newClass = arkts.factory.createETSNewClassInstanceExpression( - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('StorageLinkDecoratedVariable'), - arkts.factory.createTSTypeParameterInstantiation( - this.property.typeAnnotation ? [this.property.typeAnnotation] : [] - ) - ) - ), - args - ); - + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_DECORATED); return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - newClass + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_STORAGE_LINK, this.propertyType, args, true) ); } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - const field = createOptionalClassProperty( + const field = factory.createOptionalClassProperty( newName, this.property, - 'StorageLinkDecoratedVariable', + StateManagementTypes.STORAGE_LINK_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); const thisValue: arkts.Expression = generateThisBacking(newName, false, true); - const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( - generateGetOrSetCall(thisValue, 'set') - ); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGet - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSet + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); return [field, getter, setter]; } @@ -159,3 +106,45 @@ export class StorageLinkTranslator extends PropertyTranslator implements Initial return createSetter2(originalName, typeAnnotation, statement); } } + +export class StorageLinkInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_LINK)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_LINK)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StorageLinkDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StorageLink` and a setter with `@StorageLink` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_LINK); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StorageLinkDecoratedVariable | undefined`. + * + * @param property expecting property with `@StorageLink`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_LINK); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/types.ts b/arkui-plugins/ui-plugins/property-translators/types.ts index b791a7da1c893b054ec9c591dddd2152cd681faa..0b903d0e3586521e9f2796c7439633a2b6231949 100644 --- a/arkui-plugins/ui-plugins/property-translators/types.ts +++ b/arkui-plugins/ui-plugins/property-translators/types.ts @@ -31,4 +31,4 @@ export interface GetterSetter { export interface InitializerConstructor { cacheTranslatedInitializer(newName: string, originalName: string): void; translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[]; -} +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index cda1293fc1c39be003a8c2d37efffcd8b79ab629..8e4415efe359cd19db4c6b23481e129063e3c8c6 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -14,45 +14,99 @@ */ import * as arkts from '@koalaui/libarkts'; -import { annotation } from '../../common/arkts-utils'; -import { factory } from './factory'; - -export enum DecoratorNames { - STATE = 'State', - STORAGE_LINK = 'StorageLink', - STORAGE_PROP = 'StorageProp', - LINK = 'Link', - PROP = 'Prop', - PROVIDE = 'Provide', - CONSUME = 'Consume', - OBJECT_LINK = 'ObjectLink', - OBSERVED = 'Observed', - WATCH = 'Watch', - BUILDER_PARAM = 'BuilderParam', - BUILDER = 'Builder', - CUSTOM_DIALOG = 'CustomDialog', - LOCAL_STORAGE_PROP = 'LocalStorageProp', - LOCAL_STORAGE_LINK = 'LocalStorageLink', - REUSABLE = 'Reusable', - TRACK = 'Track', +import { ImportCollector } from '../../common/import-collector'; +import { isDecoratorAnnotation } from '../../common/arkts-utils'; +import { + DecoratorIntrinsicNames, + DecoratorNames, + StateManagementTypes, + GetSetTypes, + ObservedNames, +} from '../../common/predefines'; +import { + addMemoAnnotation, + findCanAddMemoFromParameter, + findCanAddMemoFromTypeAnnotation, +} from '../../collectors/memo-collectors/utils'; +import { CustomDialogNames } from '../utils'; +import { ReturnTransformer } from './return-transformer'; + +export interface DecoratorInfo { + annotation: arkts.AnnotationUsage; + name: DecoratorNames; } -export function collectPropertyDecorators(property: arkts.ClassProperty): string[] { - const properties: string[] = []; - property.annotations.forEach((anno) => { - if (!!anno.expr && arkts.isIdentifier(anno.expr)) { - properties.push(anno.expr.name); - } - }); - return properties; +export interface OptionalMemberInfo { + isCall?: boolean; + isNumeric?: boolean; } -export function isDecoratorAnnotation(anno: arkts.AnnotationUsage, decoratorName: DecoratorNames): boolean { +export function isDecoratorIntrinsicAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorIntrinsicNames +): boolean { return !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; } -export function hasDecorator( +export function removeDecorator( property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames, + ignoreDecl?: boolean +): void { + if (arkts.isMethodDefinition(property)) { + property.scriptFunction.setAnnotations( + property.scriptFunction.annotations.filter( + (anno) => !isDecoratorAnnotation(anno, decoratorName, ignoreDecl) + ) + ); + } else { + property.setAnnotations( + property.annotations.filter((anno) => !isDecoratorAnnotation(anno, decoratorName, ignoreDecl)) + ); + } +} + +export function getGetterReturnType(method: arkts.MethodDefinition): arkts.TypeNode | undefined { + const body = method.scriptFunction.body; + if (!body || !arkts.isBlockStatement(body) || body.statements.length <= 0) { + return undefined; + } + let returnType: arkts.TypeNode | undefined = undefined; + const returnTransformer = new ReturnTransformer(); + returnTransformer.visitor(body); + const typeArray = returnTransformer.types; + if (typeArray.length <= 0) { + returnType = undefined; + } else if (typeArray.length === 1) { + returnType = typeArray.at(0); + } else { + returnType = arkts.factory.createUnionType(typeArray); + } + returnTransformer.reset(); + return returnType?.clone(); +} + +/** + * checking whether astNode's annotations contain given corresponding decorator name, + * regardless where the annotation's declaration is from arkui declaration files. + */ +export function hasDecoratorName( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): boolean { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName, true)); + } + return property.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName, true)); +} + +export function hasDecorator( + property: + | arkts.ClassProperty + | arkts.ClassDefinition + | arkts.MethodDefinition + | arkts.ETSParameterExpression + | arkts.ETSFunctionType, decoratorName: DecoratorNames ): boolean { if (arkts.isMethodDefinition(property)) { @@ -68,34 +122,54 @@ export function hasDecorator( */ export function needDefiniteOrOptionalModifier(st: arkts.ClassProperty): boolean { return ( - hasDecorator(st, DecoratorNames.LINK) || - hasDecorator(st, DecoratorNames.CONSUME) || - hasDecorator(st, DecoratorNames.OBJECT_LINK) || - (hasDecorator(st, DecoratorNames.PROP) && !st.value) + hasDecoratorName(st, DecoratorNames.LINK) || + hasDecoratorName(st, DecoratorNames.CONSUME) || + hasDecoratorName(st, DecoratorNames.OBJECT_LINK) || + (hasDecoratorName(st, DecoratorNames.PROP) && !st.value) || + (hasDecoratorName(st, DecoratorNames.PROP_REF) && !st.value) || + (hasDecoratorName(st, DecoratorNames.PARAM) && !st.value) || + (hasDecoratorName(st, DecoratorNames.EVENT) && !st.value) || + (hasDecoratorName(st, DecoratorNames.REQUIRE) && !st.value) ); } -export function getStateManagementType(node: arkts.ClassProperty): string { - if (hasDecorator(node, DecoratorNames.STATE)) { - return 'StateDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.LINK)) { - return 'DecoratedV1VariableBase'; - } else if (hasDecorator(node, DecoratorNames.PROP)) { - return 'PropDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.STORAGE_LINK)) { - return 'StorageLinkDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.STORAGE_PROP)) { - return 'StoragePropDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.PROVIDE)) { - return 'ProvideDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.CONSUME)) { - return 'ConsumeDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.OBJECT_LINK)) { - return 'ObjectLinkDecoratedVariable'; - } else if (hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { - return 'SyncedProperty'; +export function findDecoratorByName( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): arkts.AnnotationUsage | undefined { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName, true)); + } + return property.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName, true)); +} + +export function findDecorator( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + decoratorName: DecoratorNames +): arkts.AnnotationUsage | undefined { + if (arkts.isMethodDefinition(property)) { + return property.scriptFunction.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName)); + } + return property.annotations.find((anno) => isDecoratorAnnotation(anno, decoratorName)); +} + +export function findDecoratorInfos( + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition +): DecoratorInfo[] { + const decoratorNames = Object.values(DecoratorNames); + const infos: DecoratorInfo[] = []; + for (let i = 0; i < decoratorNames.length; i++) { + const name = decoratorNames[i]; + const annotation: arkts.AnnotationUsage | undefined = findDecoratorByName(property, name); + if (!!annotation) { + infos.push({ annotation, name }); + } } - return 'MutableState'; + return infos; +} + +export function collectStateManagementTypeImport(type: StateManagementTypes): void { + ImportCollector.getInstance().collectImport(type); } export function createGetter( @@ -103,23 +177,27 @@ export function createGetter( type: arkts.TypeNode | undefined, returns: arkts.Expression, needMemo: boolean = false, + isStatic: boolean = false ): arkts.MethodDefinition { const returnType: arkts.TypeNode | undefined = type?.clone(); - if (needMemo) { - returnType?.setAnnotations([annotation('memo')]); + if (needMemo && findCanAddMemoFromTypeAnnotation(returnType)) { + addMemoAnnotation(returnType); } const body = arkts.factory.createBlock([arkts.factory.createReturnStatement(returns)]); + const modifiers = isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; const scriptFunction = arkts.factory.createScriptFunction( body, arkts.FunctionSignature.createFunctionSignature(undefined, [], returnType, false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + modifiers ); return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, arkts.factory.createIdentifier(name), scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + modifiers, false ); } @@ -144,8 +222,8 @@ export function createSetter( arkts.factory.createIdentifier('value', type?.clone()), undefined ); - if (needMemo) { - param.annotations = [annotation('memo')]; + if (needMemo && findCanAddMemoFromParameter(param)) { + addMemoAnnotation(param); } const scriptFunction = arkts.factory.createScriptFunction( body, @@ -166,25 +244,29 @@ export function createSetter( export function createSetter2( name: string, type: arkts.TypeNode | undefined, - statement: arkts.AstNode + statement: arkts.AstNode, + isStatic: boolean = false ): arkts.MethodDefinition { const body = arkts.factory.createBlock([statement]); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier('value', type?.clone()), undefined ); + const modifiers = isStatic + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; const scriptFunction = arkts.factory.createScriptFunction( body, arkts.FunctionSignature.createFunctionSignature(undefined, [param], undefined, false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_SETTER, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + modifiers ); return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, arkts.factory.createIdentifier(name), scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + modifiers, false ); } @@ -297,16 +379,7 @@ function getDifferentAnnoTypeValue(value: arkts.Expression): string | boolean { return value.dumpSrc(); } -export function judgeIfAddWatchFunc(args: arkts.Expression[], property: arkts.ClassProperty): void { - if (hasDecorator(property, DecoratorNames.WATCH)) { - const watchStr: string | undefined = getValueInAnnotation(property, DecoratorNames.WATCH); - if (watchStr) { - args.push(factory.createWatchCallback(watchStr)); - } - } -} - -export function generateGetOrSetCall(beforCall: arkts.AstNode, type: string) { +export function generateGetOrSetCall(beforCall: arkts.AstNode, type: GetSetTypes) { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( beforCall, @@ -343,25 +416,65 @@ export function generateToRecord(newName: string, originalName: string): arkts.P ); } -export function getStageManagementIdent(property: arkts.ClassProperty): string { - const useMutableState: boolean = - hasDecorator(property, DecoratorNames.STATE) || - hasDecorator(property, DecoratorNames.STORAGE_LINK) || - hasDecorator(property, DecoratorNames.PROVIDE) || - hasDecorator(property, DecoratorNames.CONSUME) || - hasDecorator(property, DecoratorNames.LINK) || - hasDecorator(property, DecoratorNames.LOCAL_STORAGE_LINK) || - hasDecorator(property, DecoratorNames.LINK); - const useSyncedProperty: boolean = - hasDecorator(property, DecoratorNames.PROP) || - hasDecorator(property, DecoratorNames.STORAGE_PROP) || - hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP) || - hasDecorator(property, DecoratorNames.OBJECT_LINK); - if (useMutableState) { - return 'MutableState'; - } else if (useSyncedProperty) { - return 'SyncedProperty'; - } else { - return ''; +export function isCustomDialogController(type: arkts.TypeNode): boolean { + if (arkts.isETSUnionType(type)) { + return !!type.types.find((item: arkts.TypeNode) => { + return isCustomDialogController(item); + }); } + return ( + !!arkts.isETSTypeReference(type) && + !!type.part && + !!type.part.name && + arkts.isIdentifier(type.part.name) && + type.part.name.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER + ); +} + +export function removeImplementProperty(originalName: string): string { + const prefix = ObservedNames.PROPERTY_PREFIX; + return originalName.substring(prefix.length); +} + +export function getValueInMonitorAnnotation(annotations: readonly arkts.AnnotationUsage[]): string[] | undefined { + const monitorAnno: arkts.AnnotationUsage | undefined = annotations.find((anno: arkts.AnnotationUsage) => { + return ( + anno.expr && + arkts.isIdentifier(anno.expr) && + anno.expr.name === DecoratorNames.MONITOR && + anno.properties.length === 1 + ); + }); + if (!monitorAnno) { + return undefined; + } + return getArrayFromAnnoProperty(monitorAnno.properties.at(0)!); +} + +export function getArrayFromAnnoProperty(property: arkts.AstNode): string[] | undefined { + if (!arkts.isClassProperty(property) || !property.value || !arkts.isArrayExpression(property.value)) { + return undefined; + } + const resArr: string[] = []; + property.value.elements.forEach((item: arkts.Expression) => { + if (arkts.isStringLiteral(item)) { + resArr.push(item.str); + } else if (arkts.isMemberExpression(item)) { + const res: string | undefined = getMonitorStrFromMemberExpr(item); + !!res && resArr.push(res); + } + }); + return resArr; +} + +function getMonitorStrFromMemberExpr(node: arkts.MemberExpression): string | undefined { + const decl: arkts.AstNode | undefined = arkts.getDecl(node.property); + if (!decl || !arkts.isClassProperty(decl) || !decl.value || !arkts.isETSNewClassInstanceExpression(decl.value)) { + return undefined; + } + const args: readonly arkts.Expression[] = decl.value.getArguments; + if (args.length >= 2 && arkts.isStringLiteral(args[1])) { + return args[1].str; + } + return undefined; } diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 6fd41c6b6cc8d05190a06545cc47e50267a78518..dedd4331e5dced6c8a4d5631327940e66340bf68 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -14,99 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { BuilderLambdaNames, CustomComponentNames, Dollars } from '../utils'; -import { factory as uiFactory } from '../ui-factory'; -import { annotation } from '../../common/arkts-utils'; +import { + BuilderLambdaNames, + CustomComponentNames, + CustomDialogNames, + getCustomComponentOptionsName, + getGettersFromClassDecl, + getTypeNameFromTypeParameter, + getTypeParamsFromClassDecl, + isCustomComponentInterface, + isCustomDialogControllerOptions, + isKnownMethodDefinition, + isStatic, +} from '../utils'; +import { factory as UIFactory } from '../ui-factory'; +import { factory as PropertyFactory } from '../property-translators/factory'; +import { factory as BuilderLambdaFactory } from '../builder-lambda-translators/factory'; +import { BuilderFactory } from '../builder-lambda-translators/builder-factory'; +import { backingField, collect, filterDefined } from '../../common/arkts-utils'; +import { + classifyInObservedClass, + classifyPropertyInInterface, + classifyStructMembers, + ClassScopeInfo, + InterfacePropertyTranslator, + PropertyTranslator, +} from '../property-translators'; +import { + CustomComponentScopeInfo, + isEtsGlobalClass, + ResourceInfo, + checkRawfileResource, + generateResourceModuleName, + generateResourceBundleName, + isDynamicName, + preCheckResourceData, + ResourceParameter, + getResourceParams, + isResourceNode, + isForEachCall, + getCustomDialogController, + isInvalidDialogControllerOptions, + findBuilderIndexInControllerOptions, + ObservedAnnoInfo, + getNoTransformationMembersInClass, + isComputedMethod, +} from './utils'; +import { collectStateManagementTypeImport, generateThisBacking, hasDecorator } from '../property-translators/utils'; +import { ProjectConfig } from '../../common/plugin-context'; +import { ImportCollector } from '../../common/import-collector'; +import { + AnimationNames, + ARKUI_COMPONENT_COMMON_SOURCE_NAME, + DecoratorNames, + Dollars, + ModuleType, + StateManagementTypes, + RESOURCE_TYPE, + ARKUI_BUILDER_SOURCE_NAME, + TypeNames, +} from '../../common/predefines'; +import { ObservedTranslator } from '../property-translators/index'; +import { + addMemoAnnotation, + collectMemoableInfoInFunctionReturnType, + collectScriptFunctionReturnTypeFromInfo, +} from '../../collectors/memo-collectors/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; +import { GenSymGenerator } from '../../common/gensym-generator'; +import { MethodTranslator } from 'ui-plugins/property-translators/base'; +import { MonitorCache } from '../property-translators/cache/monitorCache'; +import { PropertyCache } from '../property-translators/cache/propertyCache'; export class factory { - /* - * create `constructor() {}`. - */ - static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, - member.name, - member.scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, - false - ); - } - - /* - * 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 - ) - .setAnnotations([annotation('memo')]); - - 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(). + /** + * update class `constructor` to private. + * @deprecated */ - static generateTransformedResource( - resourceNode: arkts.CallExpression, - newArgs: arkts.AstNode[] - ): arkts.CallExpression { - const transformedKey: string = - resourceNode.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE ? '_r' : '_rawfile'; - return arkts.factory.updateCallExpression( - resourceNode, - arkts.factory.createIdentifier(transformedKey), - resourceNode.typeArguments, - newArgs - ); + static setStructConstructorToPrivate(member: arkts.MethodDefinition): arkts.MethodDefinition { + member.modifiers &= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + member.modifiers &= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED; + member.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE; + return member; } - /* + /** * create __initializeStruct method. */ - static createInitializeStruct( - structInfo: arkts.StructInfo, - optionsTypeName: string, - isDecl?: boolean - ): arkts.MethodDefinition { + static createInitializeStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const updateKey: arkts.Identifier = arkts.factory.createIdentifier( CustomComponentNames.COMPONENT_INITIALIZE_STRUCT ); let body: arkts.BlockStatement | undefined; - let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; - if (!isDecl) { - body = arkts.factory.createBlock(structInfo.initializeBody); + let modifiers: arkts.Es2pandaModifierFlags = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE; + if (!scope.isDecl) { + body = arkts.factory.createBlock([ + ...PropertyCache.getInstance().getInitializeBody(scope.name), + ...MonitorCache.getInstance().getCachedMonitors(scope.name), + ]); modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; } const scriptFunction: arkts.ScriptFunction = arkts.factory @@ -114,7 +121,7 @@ export class factory { body, arkts.FunctionSignature.createFunctionSignature( undefined, - [uiFactory.createInitializersOptionsParameter(optionsTypeName), uiFactory.createContentParameter()], + [UIFactory.createInitializersOptionsParameter(optionsTypeName), UIFactory.createContentParameter()], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -132,22 +139,67 @@ export class factory { ); } - /* + static transformControllerInterfaceType(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!node.body || node.body.body.length <= 0 || !arkts.isMethodDefinition(node.body.body[0])) { + return node; + } + const updatedBody = arkts.factory.updateInterfaceBody(node.body, [ + this.updateBuilderType(node.body.body[0]), + ...node.body.body.slice(1), + ]); + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + updatedBody, + node.isStatic, + node.isFromExternal + ); + } + + static updateBuilderType(builderNode: arkts.MethodDefinition): arkts.MethodDefinition { + const newType: arkts.TypeNode | undefined = UIFactory.createTypeReferenceFromString( + CustomDialogNames.CUSTOM_BUILDER + ); + if (builderNode.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + const newOverLoads = builderNode.overloads.map((overload) => { + if (arkts.isMethodDefinition(overload)) { + return factory.updateBuilderType(overload); + } + return overload; + }); + builderNode.setOverloads(newOverLoads); + if (!!newType) { + builderNode.scriptFunction.setReturnTypeAnnotation(newType); + } + } else if (builderNode.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + const param = builderNode.scriptFunction.params[0] as arkts.ETSParameterExpression; + const newParam: arkts.Expression | undefined = arkts.factory.updateParameterDeclaration( + param, + arkts.factory.createIdentifier(param.identifier.name, newType), + param.initializer + ); + if (!!newParam) { + return UIFactory.updateMethodDefinition(builderNode, { function: { params: [newParam] } }); + } + } + return builderNode; + } + + /** * create __updateStruct method. */ - static createUpdateStruct( - structInfo: arkts.StructInfo, - optionsTypeName: string, - isDecl?: boolean - ): arkts.MethodDefinition { + static createUpdateStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const updateKey: arkts.Identifier = arkts.factory.createIdentifier( CustomComponentNames.COMPONENT_UPDATE_STRUCT ); let body: arkts.BlockStatement | undefined; - let modifiers: arkts.Es2pandaModifierFlags = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT; - if (!isDecl) { - body = arkts.factory.createBlock(structInfo.updateBody); + let modifiers: arkts.Es2pandaModifierFlags = + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE; + if (!scope.isDecl) { + body = arkts.factory.createBlock(PropertyCache.getInstance().getUpdateBody(scope.name)); modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; } @@ -156,7 +208,7 @@ export class factory { body, arkts.FunctionSignature.createFunctionSignature( undefined, - [uiFactory.createInitializersOptionsParameter(optionsTypeName)], + [UIFactory.createInitializersOptionsParameter(optionsTypeName)], arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), false ), @@ -174,15 +226,15 @@ export class factory { ); } - /* + /** * create __toRecord method when the component is decorated with @Reusable. */ - static toRecord(optionsTypeName: string, toRecordBody: arkts.Property[]): arkts.MethodDefinition { + static createToRecord(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { const paramsCasted = factory.generateParamsCasted(optionsTypeName); const returnRecord = arkts.factory.createReturnStatement( arkts.ObjectExpression.createObjectExpression( arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - toRecordBody, + PropertyCache.getInstance().getToRecordBody(scope.name), false ) ); @@ -209,7 +261,7 @@ export class factory { ); } - /* + /** * generate `const paramsCasted = (params as )`. */ static generateParamsCasted(optionsTypeName: string): arkts.VariableDeclaration { @@ -230,7 +282,7 @@ export class factory { ); } - /* + /** * generate Record type. */ static generateTypeRecord(): arkts.ETSTypeReference { @@ -245,7 +297,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static generateTypeReferenceWithTypeName(typeName: string): arkts.ETSTypeReference { @@ -254,7 +306,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static updateCustomComponentClass( @@ -275,15 +327,20 @@ export class factory { ); } - /* - * add headers for animation in CommonMethod + /** + * add headers for animation & @AnimatableExtend in CommonMethod */ - static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.AstNode { - const animationStart = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_START); - const animationStop = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_STOP); + static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!node.body) { + return node; + } + const animationStart = factory.createAnimationMethod(AnimationNames.ANIMATION_START); + const animationStop = factory.createAnimationMethod(AnimationNames.ANIMATION_STOP); + const createOrSetAniProperty = factory.createOrSetAniProperty(); const updatedBody = arkts.factory.updateInterfaceBody(node.body!, [ animationStart, animationStop, + createOrSetAniProperty, ...node.body!.body, ]); return arkts.factory.updateInterfaceDeclaration( @@ -297,7 +354,70 @@ export class factory { ); } - /* + /** + * helper to create value parameter for AnimatableExtend methods + */ + static createAniExtendValueParam(): arkts.ETSParameterExpression { + const numberType = UIFactory.createTypeReferenceFromString('number'); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([UIFactory.createTypeReferenceFromString('T')]) + ) + ); + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ); + } + + /** + * generate __createOrSetAnimatableProperty(...) for AnimatableExtend + */ + static createOrSetAniProperty(): arkts.MethodDefinition { + const funcNameParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('functionName', UIFactory.createTypeReferenceFromString('string')), + undefined + ); + const cbParam = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'callback', + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [factory.createAniExtendValueParam()], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + UIFactory.createScriptFunction({ + typeParams: arkts.factory.createTypeParameterDeclaration( + [arkts.factory.createTypeParameter(arkts.factory.createIdentifier('T'))], + 0 + ), + params: [funcNameParam, factory.createAniExtendValueParam(), cbParam], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + }), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /** * generate animationStart(...) and animationStop(...) */ static createAnimationMethod(key: string): arkts.MethodDefinition { @@ -305,19 +425,17 @@ export class factory { arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( 'value', - arkts.factory.createUnionType( - [ - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) - ), - arkts.factory.createETSUndefinedType() - ] - ) + arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) + ), + arkts.factory.createETSUndefinedType(), + ]) ), undefined ), ]; - const aniFuncExpr = arkts.factory.createScriptFunction( + const aniFunc = arkts.factory.createScriptFunction( undefined, arkts.factory.createFunctionSignature(undefined, aniparams, arkts.TSThisType.createTSThisType(), false), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, @@ -326,35 +444,627 @@ export class factory { return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, arkts.factory.createIdentifier(key), - aniFuncExpr, + aniFunc, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); } + /** + * transform property members in custom-component class. + */ + static tranformPropertyMembers( + propertyTranslators: (PropertyTranslator | MethodTranslator)[], + optionsTypeName: string, + scope: CustomComponentScopeInfo + ): arkts.AstNode[] { + const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); + const collections = []; + if (!scope.hasInitializeStruct) { + collections.push(this.createInitializeStruct(optionsTypeName, scope)); + } + if (!scope.hasUpdateStruct) { + collections.push(this.createUpdateStruct(optionsTypeName, scope)); + } + if (!!scope.annotations?.reusable) { + collections.push(this.createToRecord(optionsTypeName, scope)); + } + return collect(...collections, ...propertyMembers); + } + + /** + * transform non-property members in custom-component class. + */ + static transformNonPropertyMembersInClass(member: arkts.AstNode, isDecl?: boolean): arkts.AstNode { + if (arkts.isMethodDefinition(member)) { + PropertyFactory.addMemoToBuilderClassMethod(member); + if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { + addMemoAnnotation(member.scriptFunction); + return BuilderFactory.rewriteBuilderMethod(member); + } + return member; + } + return member; + } + + /** + * transform members in custom-component class. + */ + static tranformClassMembers(node: arkts.ClassDeclaration, scope: CustomComponentScopeInfo): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + let classOptionsName: string | undefined; + if (scope.isDecl) { + const [_, classOptions] = getTypeParamsFromClassDecl(node); + classOptionsName = getTypeNameFromTypeParameter(classOptions); + } + const definition: arkts.ClassDefinition = node.definition; + const className: string | undefined = node.definition.ident?.name; + if (!className) { + throw new Error('Non Empty className expected for Component'); + } + const body: readonly arkts.AstNode[] = definition.body; + const propertyTranslators: (PropertyTranslator | MethodTranslator)[] = filterDefined( + body.map((member) => classifyStructMembers(member, scope)) + ); + const translatedMembers: arkts.AstNode[] = this.tranformPropertyMembers( + propertyTranslators, + classOptionsName ?? getCustomComponentOptionsName(className), + scope + ); + const updateMembers: arkts.AstNode[] = body + .filter((member) => !arkts.isClassProperty(member) && !isComputedMethod(member)) + .map((member: arkts.AstNode) => factory.transformNonPropertyMembersInClass(member, scope.isDecl)); + const updateClassDef: arkts.ClassDefinition = this.updateCustomComponentClass( + definition, + factory.addClassStaticBlock([...translatedMembers, ...updateMembers], body) + ); + if ( + !!scope.annotations.customdialog || + (scope.isDecl && scope.name === CustomComponentNames.BASE_CUSTOM_DIALOG_NAME) + ) { + updateClassDef.addProperties(factory.addControllerSetMethod(scope.isDecl, body)); + } + return arkts.factory.updateClassDeclaration(node, updateClassDef); + } + + /** + * Determine whether a class static block needs to be added. + */ + static addClassStaticBlock(members: arkts.AstNode[], body: readonly arkts.AstNode[]): arkts.AstNode[] { + const staticBlock = body.find((item: arkts.AstNode) => arkts.isClassStaticBlock(item)); + if (staticBlock) { + return members; + } + const classHasStaticComputed = body.find((item: arkts.AstNode) => isComputedMethod(item) && isStatic(item)); + if (classHasStaticComputed) { + members.push(UIFactory.createClassStaticBlock()); + } + return members; + } + + /** + * add `__setDialogController__` method in `@CustomDialog` component. + */ + static addControllerSetMethod(isDecl: boolean, body: readonly arkts.AstNode[]): arkts.MethodDefinition[] { + if (isDecl) { + return [this.createCustomDialogMethod(isDecl)]; + } + const dialogControllerProperty: arkts.ClassProperty | undefined = body.find( + (item: arkts.AstNode) => arkts.isClassProperty(item) && getCustomDialogController(item).length > 0 + ) as arkts.ClassProperty | undefined; + if (!!dialogControllerProperty) { + return [this.createCustomDialogMethod(isDecl, getCustomDialogController(dialogControllerProperty))]; + } + return []; + } + + /** + * transform `$r` and `$rawfile` function calls. + */ + static transformResource( + resourceNode: arkts.CallExpression, + projectConfig: ProjectConfig | undefined, + resourceInfo: ResourceInfo + ): arkts.CallExpression { + if (!arkts.isIdentifier(resourceNode.expression) || !projectConfig) { + return resourceNode; + } + const resourceKind: Dollars = resourceNode.expression.name as Dollars; + if (arkts.isStringLiteral(resourceNode.arguments[0])) { + return factory.processStringLiteralResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceKind, + resourceNode.arguments[0] + ); + } else if (resourceNode.arguments && resourceNode.arguments.length) { + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + -1, + resourceKind === Dollars.DOLLAR_RAWFILE ? RESOURCE_TYPE.rawfile : -1, + Array.from(resourceNode.arguments) + ), + '', + false, + projectConfig, + resourceKind + ); + } + return resourceNode; + } + /* - * generate XComponent(..., packageInfo: string, ...) + * Process string Literal type arguments for resource node. */ - static modifyXcomponent(node: arkts.ScriptFunction): arkts.ScriptFunction { - const info = arkts.factory.createParameterDeclaration( + static processStringLiteralResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceKind: Dollars, + literalArg: arkts.StringLiteral + ): arkts.CallExpression { + const resourceData: string[] = literalArg.str.trim().split('.'); + const fromOtherModule: boolean = !!resourceData.length && /^\[.*\]$/g.test(resourceData[0]); + if (resourceKind === Dollars.DOLLAR_RAWFILE) { + checkRawfileResource(resourceNode, literalArg, fromOtherModule, resourceInfo.rawfile); + let resourceId: number = projectConfig.moduleType === ModuleType.HAR ? -1 : 0; + let resourceModuleName: string = ''; + if (resourceData && resourceData[0] && fromOtherModule) { + resourceId = -1; + resourceModuleName = resourceData[0]; + } + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams(resourceId, RESOURCE_TYPE.rawfile, [literalArg]), + resourceModuleName, + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RAWFILE + ); + } else { + return factory.processStringLiteralDollarResourceNode( + resourceNode, + resourceInfo, + projectConfig, + resourceData, + fromOtherModule + ); + } + } + + static createCustomDialogMethod(isDecl: boolean, controller?: string): arkts.MethodDefinition { + let block: arkts.BlockStatement | undefined = undefined; + if (!!controller) { + block = arkts.factory.createBlock(this.createSetControllerElements(controller)); + } + const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( - 'packageInfo', - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('string') - ) - ) + CustomDialogNames.CONTROLLER, + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) ), undefined ); + const modifiers = isDecl + ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE + : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(CustomDialogNames.SET_DIALOG_CONTROLLER_METHOD), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: block, + params: [param], + returnTypeAnnotation: arkts.factory.createPrimitiveType( + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID + ), + hasReceiver: false, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + modifiers: modifiers, + }, + modifiers: modifiers, + }); + } + + /* + * create assignment expression `this.__backing = controller`. + */ + static createSetControllerElements(controller: string): arkts.AstNode[] { + return controller.length !== 0 + ? [ + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + generateThisBacking(backingField(controller)), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier(CustomDialogNames.CONTROLLER) + ) + ), + ] + : []; + } + + /* + * Process string Literal type arguments for $r node. + */ + static processStringLiteralDollarResourceNode( + resourceNode: arkts.CallExpression, + resourceInfo: ResourceInfo, + projectConfig: ProjectConfig, + resourceData: string[], + fromOtherModule: boolean + ): arkts.CallExpression { + if ( + preCheckResourceData(resourceNode, resourceData, resourceInfo.resourcesList, fromOtherModule, projectConfig) + ) { + const resourceId: number = + projectConfig.moduleType === ModuleType.HAR || + fromOtherModule || + !resourceInfo.resourcesList[resourceData[0]] + ? -1 + : resourceInfo.resourcesList[resourceData[0]].get(resourceData[1])![resourceData[2]]; + return factory.generateTransformedResourceCall( + resourceNode, + getResourceParams( + resourceId, + RESOURCE_TYPE[resourceData[1].trim()], + projectConfig.moduleType === ModuleType.HAR || fromOtherModule + ? Array.from(resourceNode.arguments) + : Array.from(resourceNode.arguments.slice(1)) + ), + resourceData.length ? resourceData[0] : '', + fromOtherModule, + projectConfig, + Dollars.DOLLAR_RESOURCE + ); + } + return resourceNode; + } + + /* + * generate tramsformed resource node, e.g. {id, type, params, bundleName, moduleName}. + */ + static generateTransformedResourceCall( + resourceNode: arkts.CallExpression, + resourceParams: ResourceParameter, + resourceModuleName: string, + fromOtherModule: boolean, + projectConfig: ProjectConfig, + resourceKind: Dollars + ): arkts.CallExpression { + const transformedKey: string = + resourceKind === Dollars.DOLLAR_RESOURCE + ? Dollars.TRANSFORM_DOLLAR_RESOURCE + : Dollars.TRANSFORM_DOLLAR_RAWFILE; + ImportCollector.getInstance().collectImport(transformedKey); + const isDynamicBundleOrModule: boolean = isDynamicName(projectConfig); + const args: arkts.AstNode[] = [ + arkts.factory.createNumericLiteral(resourceParams.id), + arkts.factory.createNumericLiteral(resourceParams.type), + arkts.factory.createStringLiteral(generateResourceBundleName(projectConfig, isDynamicBundleOrModule)), + arkts.factory.createStringLiteral( + generateResourceModuleName(projectConfig, isDynamicBundleOrModule, resourceModuleName, fromOtherModule) + ), + ...resourceParams.params, + ]; + return arkts.factory.updateCallExpression( + resourceNode, + arkts.factory.createIdentifier(transformedKey), + undefined, + args + ); + } + + /** + * transform members in interface. + */ + static tranformInterfaceMembers( + node: arkts.TSInterfaceDeclaration, + externalSourceName?: string + ): arkts.TSInterfaceDeclaration { + if (!node.id || !node.body) { + return node; + } + if (externalSourceName === ARKUI_COMPONENT_COMMON_SOURCE_NAME && node.id.name === 'CommonMethod') { + return factory.modifyExternalComponentCommon(node); + } + if (isCustomDialogControllerOptions(node, externalSourceName)) { + return factory.transformControllerInterfaceType(node); + } + if (isCustomComponentInterface(node)) { + return factory.tranformCustomComponentInterfaceMembers(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 + ); + } + + /** + * transform members in custom-component related interface. + */ + static tranformCustomComponentInterfaceMembers(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + const propertyTranslators: InterfacePropertyTranslator[] = filterDefined( + node.body!.body.map((it) => classifyPropertyInInterface(it)) + ); + + let shouldUpdate: boolean = false; + const newBody = propertyTranslators.map((translator) => { + const newProperty = translator.translateProperty(); + shouldUpdate ||= translator.modified; + return newProperty; + }); + + if (shouldUpdate) { + return arkts.factory.updateInterfaceDeclaration( + node, + node.extends, + node.id, + node.typeParams, + arkts.factory.updateInterfaceBody(node.body!, newBody), + node.isStatic, + node.isFromExternal + ); + } + + return node; + } + + static transformETSGlobalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + const updatedBody = node.definition.body.map((member: arkts.AstNode) => { + arkts.isMethodDefinition(member) && PropertyFactory.addMemoToBuilderClassMethod(member); + if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { + member = arkts.factory.updateMethodDefinition( + member, + member.kind, + member.name, + factory.transformAnimatableExtend(member.scriptFunction), + member.modifiers, + false + ); + } + return member; + }); + let newStatements: arkts.AstNode[] = []; + if (externalSourceName === ARKUI_BUILDER_SOURCE_NAME) { + newStatements.push(...BuilderLambdaFactory.addConditionBuilderDecls()); + } + 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, + [...updatedBody, ...newStatements], + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); + } + + static transformNormalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + if (isEtsGlobalClass(node)) { + return this.transformETSGlobalClass(node, externalSourceName); + } + const newClassDef = factory.updateObservedTrackClassDef(node.definition); + return arkts.factory.updateClassDeclaration(node, newClassDef); + } + + /** + * transform class definition with , `@ObservedV2` and `@Trace`, `@Observed` or `@Trace`. + */ + static updateObservedTrackClassDef(node: arkts.ClassDefinition): arkts.ClassDefinition { + const isObserved: boolean = hasDecorator(node, DecoratorNames.OBSERVED); + const isObservedV2: boolean = hasDecorator(node, DecoratorNames.OBSERVED_V2); + const classHasTrack: boolean = node.body.some( + (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACK) + ); + const classHasTrace: boolean = node.body.some( + (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACE) + ); + const className: string | undefined = node.ident?.name; + if (!className || !(isObserved || classHasTrack || isObservedV2)) { + return node; + } + const ObservedAnno: ObservedAnnoInfo = { isObserved, classHasTrack, isObservedV2, classHasTrace, className }; + const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( + node, + node.ident, + node.typeParams, + node.superTypeParams, + [ + ...node.implements, + arkts.TSClassImplements.createTSClassImplements( + UIFactory.createTypeReferenceFromString(StateManagementTypes.OBSERVED_OBJECT) + ), + arkts.TSClassImplements.createTSClassImplements( + UIFactory.createTypeReferenceFromString(StateManagementTypes.SUBSCRIBED_WATCHES) + ), + ], + undefined, + node.super, + factory.observedTrackPropertyMembers(node, ObservedAnno), + node.modifiers, + arkts.classDefinitionFlags(node) + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVED_OBJECT); + return updateClassDef; + } + + static observedTrackPropertyMembers( + definition: arkts.ClassDefinition, + ObservedAnno: ObservedAnnoInfo + ): arkts.AstNode[] { + const watchMembers: arkts.AstNode[] = PropertyFactory.createWatchMembers(); + const v1RenderIdMembers: arkts.AstNode[] = PropertyFactory.createV1RenderIdMembers(ObservedAnno.isObservedV2); + const conditionalAddRef: arkts.MethodDefinition = PropertyFactory.conditionalAddRef(ObservedAnno.isObservedV2); + const getters: arkts.MethodDefinition[] = getGettersFromClassDecl(definition); + const classScopeInfo: ClassScopeInfo = { + isObserved: ObservedAnno.isObserved, + classHasTrack: ObservedAnno.classHasTrack, + isObservedV2: ObservedAnno.isObservedV2, + classHasTrace: ObservedAnno.classHasTrace, + className: ObservedAnno.className, + getters: getters, + }; + const body: readonly arkts.AstNode[] = definition.body; + const translators: (ObservedTranslator | MethodTranslator)[] = filterDefined( + body.map((it) => classifyInObservedClass(it, classScopeInfo)) + ); + const metaProperty: arkts.ClassProperty[] = ObservedAnno.classHasTrack + ? [] + : [PropertyFactory.createMetaInObservedClass()]; + const propertyMembers = translators.map((translator) => translator.translateMember()); + const restMembers: arkts.AstNode[] = getNoTransformationMembersInClass(definition, ObservedAnno); + const returnNodes = factory.addClassStaticBlock( + [ + ...[...watchMembers, ...v1RenderIdMembers, conditionalAddRef], + ...(ObservedAnno.isObserved ? metaProperty : []), + ...collect(...propertyMembers), + ...restMembers, + ...classScopeInfo.getters, + ], + body + ); + return ObservedAnno.isObservedV2 + ? returnNodes.concat(this.transformObservedV2Constuctor(definition, classScopeInfo.className)) + : returnNodes; + } + + static transformObservedV2Constuctor(definition: arkts.ClassDefinition, className: string): arkts.MethodDefinition { + const addConstructorNodes: arkts.AstNode[] = MonitorCache.getInstance().getCachedMonitors(className); + let originConstructorMethod: arkts.MethodDefinition | undefined = definition.body.find( + (it) => + arkts.isMethodDefinition(it) && + isKnownMethodDefinition(it, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) + ) as arkts.MethodDefinition | undefined; + const isDecl: boolean = arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + if (!originConstructorMethod) { + return UIFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI), + function: { + key: arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI), + body: isDecl ? undefined : arkts.factory.createBlock(addConstructorNodes), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, + }, + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, + }); + } + if (isDecl) { + return originConstructorMethod; + } + const originBody = originConstructorMethod.scriptFunction.body as arkts.BlockStatement | undefined; + return UIFactory.updateMethodDefinition(originConstructorMethod, { + function: { + body: originBody + ? arkts.factory.updateBlock(originBody, [...originBody.statements, ...addConstructorNodes]) + : arkts.factory.createBlock(addConstructorNodes), + }, + }); + } + + /* + * helper for transformAnimatableExtend to create callback argument in __createAnimatableProperty + */ + static createAniExtendCbArg( + param: arkts.ETSParameterExpression, + originStatements: arkts.Statement[] + ): arkts.ArrowFunctionExpression { + const assignmentExpr = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + param.identifier.clone(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createTSAsExpression(param.identifier.clone(), param.type as arkts.TypeNode, false) + ) + ); + const numberType = UIFactory.createTypeReferenceFromString('number'); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([param.type as arkts.TypeNode]) + ) + ); + ImportCollector.getInstance().collectImport(AnimationNames.ANIMATABLE_ARITHMETIC); + return arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([assignmentExpr, ...originStatements]), + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + param.identifier.name, + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ), + ], + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + }) + ); + } + + /* + * transform @AnimatableExtend method + */ + static transformAnimatableExtend(node: arkts.ScriptFunction): arkts.ScriptFunction { + if (!arkts.isEtsParameterExpression(node.params[1]) || !node.body || !arkts.isBlockStatement(node.body)) { + return node; + } + const funcName: arkts.StringLiteral = arkts.factory.createStringLiteral(node.id?.name!); + const paramValue: arkts.ETSParameterExpression = node.params[1]; + const originStatements: arkts.Statement[] = [...node.body.statements]; + const createOrSetStatement = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + funcName, + paramValue.identifier, + factory.createAniExtendCbArg(paramValue, originStatements.slice(0, -1)), + ] + ) + ); return arkts.factory.updateScriptFunction( node, - node.body, - arkts.factory.createFunctionSignature( + arkts.factory.createBlock([createOrSetStatement, originStatements[originStatements.length - 1]]), + arkts.FunctionSignature.createFunctionSignature( node.typeParams, - [...node.params.slice(0, 2), info, ...node.params.slice(2)], + node.params, node.returnTypeAnnotation, - false + node.hasReceiver ), node.flags, node.modifiers @@ -362,24 +1072,270 @@ export class factory { } /* - * transform ExternalSource headers + * add arrow function type to arguments of call expression. */ - static transformExternalSource(externalSourceName: string, node: arkts.AstNode): arkts.AstNode { + static transformCallArguments(node: arkts.CallExpression): arkts.CallExpression { + const newFunc = UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([arkts.factory.createReturnStatement(node.arguments[0])]), + returnTypeAnnotation: this.getReturnTypeWithArrowParameter(node) ?? this.getReturnTypeWithTsType(node), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }); + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(newFunc); + collectScriptFunctionReturnTypeFromInfo(newFunc, returnMemoableInfo); + const newArrowArg: arkts.ArrowFunctionExpression = arkts.factory.createArrowFunction(newFunc); + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ + newArrowArg, + ...node.arguments.slice(1), + ]); + } + + static getReturnTypeWithArrowParameter(node: arkts.CallExpression): arkts.TypeNode | undefined { + const secondArg: arkts.Expression = node.arguments[1]; + if (!arkts.isArrowFunctionExpression(secondArg) || secondArg.scriptFunction.params.length <= 0) { + return undefined; + } + const argTypeParam: arkts.Expression = secondArg.scriptFunction.params[0]; + if (!arkts.isEtsParameterExpression(argTypeParam)) { + return undefined; + } + const type: arkts.AstNode | undefined = argTypeParam.type; + if (type && arkts.isTypeNode(type)) { + return UIFactory.createComplexTypeFromStringAndTypeParameter(TypeNames.ARRAY, [type.clone()]); + } + return undefined; + } + + static getReturnTypeWithTsType(node: arkts.CallExpression): arkts.TypeNode | undefined { + if (node.typeArguments?.length) { + return UIFactory.createComplexTypeFromStringAndTypeParameter(TypeNames.ARRAY, node.typeArguments); + } + const firstArg: arkts.Expression = node.arguments[0]; + const type = arkts.createTypeNodeFromTsType(firstArg); + if (!type || !arkts.isTypeNode(type)) { + return undefined; + } + return type; + } + + static AddArrowTypeForParameter(node: arkts.MethodDefinition): arkts.MethodDefinition { + if (node.scriptFunction.params.length < 2) { + return node; + } + const paramFirst = node.scriptFunction.params[0]; + if (!arkts.isEtsParameterExpression(paramFirst) || !paramFirst.type || !arkts.isTypeNode(paramFirst.type)) { + return node; + } + const script = UIFactory.updateScriptFunction(node.scriptFunction, { + params: [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + paramFirst.identifier.name, + UIFactory.createLambdaFunctionType([], paramFirst.type) + ), + undefined + ), + ...node.scriptFunction.params.slice(1), + ], + }); + return arkts.factory.updateMethodDefinition(node, node.kind, node.name, script, node.modifiers, false); + } + + static transformCallExpression( + node: arkts.CallExpression, + projectConfig: ProjectConfig | undefined, + resourceInfo: ResourceInfo + ): arkts.CallExpression { + if (arkts.isCallExpression(node) && isResourceNode(node)) { + return this.transformResource(node, projectConfig, resourceInfo); + } + if (arkts.isCallExpression(node) && isForEachCall(node)) { + return this.transformCallArguments(node); + } + if (isArkUICompatible(node)) { + return generateArkUICompatible(node as arkts.CallExpression); + } + return node; + } + + static transformCustomDialogController( + node: arkts.ETSNewClassInstanceExpression + ): arkts.ETSNewClassInstanceExpression | arkts.Expression { + if (isInvalidDialogControllerOptions(node.getArguments)) { + return node; + } + const optionArg = node.getArguments[0]; + const options: arkts.ObjectExpression = arkts.isObjectExpression(optionArg) + ? optionArg + : ((optionArg as arkts.TSAsExpression).expr as arkts.ObjectExpression); + const properties = options.properties as arkts.Property[]; + const builderIndex: number = findBuilderIndexInControllerOptions(properties); + if (builderIndex < 0 || !properties.at(builderIndex)!.value) { + return node; + } + const builder: arkts.Property = properties.at(builderIndex)!; + const gensymName: string = GenSymGenerator.getInstance().id(); + const newBuilderValue = this.createDialogBuilderArrow(builder.value!, gensymName); + const newProperty = arkts.factory.updateProperty(builder, builder.key, newBuilderValue); + const newObj = arkts.factory.updateObjectExpression( + options, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + ...(options.properties as arkts.Property[]).slice(0, builderIndex), + newProperty, + ...(options.properties as arkts.Property[]).slice(builderIndex + 1), + this.createBaseComponent(), + ], + false + ); + const newOptions = arkts.isTSAsExpression(optionArg) + ? arkts.factory.updateTSAsExpression(optionArg, newObj, optionArg.typeAnnotation, optionArg.isConst) + : newObj; + const typeRef = node.getTypeRef as arkts.ETSTypeReference; + const newNode = arkts.factory.updateETSNewClassInstanceExpression(node, typeRef, [newOptions]); + return factory.createBlockStatementForOptionalExpression(newNode, gensymName); + } + + static createDialogBuilderArrow(value: arkts.Expression, gensymName: string): arkts.Expression { if ( - externalSourceName === 'arkui.component.common' && - arkts.isTSInterfaceDeclaration(node) && - !!node.id && - node.id.name === 'CommonMethod' + arkts.isCallExpression(value) && + arkts.isMemberExpression(value.expression) && + arkts.isIdentifier(value.expression.property) && + value.expression.property.name === BuilderLambdaNames.TRANSFORM_METHOD_NAME ) { - return factory.modifyExternalComponentCommon(node); - } else if ( - externalSourceName === 'arkui.component.xcomponent' && - arkts.isScriptFunction(node) && - !!node.id && - node.id.name === 'XComponent' + return addMemoAnnotation( + arkts.factory.createArrowFunction( + UIFactory.createScriptFunction({ + body: arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + this.transformCustomDialogComponentCall(value, gensymName) + ), + ]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ) + ); + } + if (arkts.isArrowFunctionExpression(value)) { + return addMemoAnnotation(value); + } + return value; + } + + static transformCustomDialogComponentCall(value: arkts.CallExpression, gensymName: string): arkts.CallExpression { + if (value.arguments.length >= 2 && arkts.isArrowFunctionExpression(value.arguments[1])) { + const originScript: arkts.ScriptFunction = value.arguments[1].scriptFunction; + const newScript: arkts.ScriptFunction = UIFactory.updateScriptFunction(originScript, { + body: this.generateInstanceSetController(originScript.body, gensymName), + }); + return arkts.factory.updateCallExpression(value, value.expression, value.typeArguments, [ + value.arguments[0], + arkts.factory.updateArrowFunction(value.arguments[1], newScript), + ...value.arguments.slice(2), + ]); + } + return value; + } + + static generateInstanceSetController( + body: arkts.AstNode | undefined, + gensymName: string + ): arkts.AstNode | undefined { + if ( + !!body && + arkts.isBlockStatement(body) && + body.statements.length > 0 && + arkts.isReturnStatement(body.statements[0]) ) { - return factory.modifyXcomponent(node); + const instanceIdent: arkts.Identifier = arkts.factory.createIdentifier( + BuilderLambdaNames.STYLE_ARROW_PARAM_NAME + ); + return arkts.factory.updateBlock(body, [ + arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONST, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + instanceIdent, + body.statements[0].argument?.clone() + ), + ] + ), + this.genertateControllerSetCall(instanceIdent, gensymName), + arkts.factory.createReturnStatement(instanceIdent.clone()), + ]); } - return node; + return body; + } + + static genertateControllerSetCall(instanceIdent: arkts.Identifier, gensymName: string): arkts.AstNode { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + instanceIdent.clone(), + arkts.factory.createIdentifier(CustomDialogNames.SET_DIALOG_CONTROLLER_METHOD), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + arkts.factory.createIdentifier(gensymName), + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER), + false + ), + ] + ) + ); + } + + static createBaseComponent(): arkts.Property { + return arkts.factory.createProperty( + arkts.factory.createIdentifier(CustomDialogNames.BASE_COMPONENT), + arkts.factory.createThisExpression() + ); + } + + static generateLetVariableDecl(left: arkts.Identifier): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + left, + undefined + ), + ] + ); + } + + static createBlockStatementForOptionalExpression( + newNode: arkts.ETSNewClassInstanceExpression, + gensymName: string + ): arkts.Expression { + const statements: arkts.Statement[] = [ + factory.generateLetVariableDecl( + arkts.factory.createIdentifier(gensymName, UIFactory.createTypeReferenceFromString(TypeNames.ANY)) + ), + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + arkts.factory.createIdentifier(gensymName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + newNode + ) + ), + arkts.factory.createExpressionStatement( + arkts.factory.createTSAsExpression( + arkts.factory.createIdentifier(gensymName), + UIFactory.createTypeReferenceFromString(CustomDialogNames.CUSTOM_DIALOG_CONTROLLER), + false + ) + ), + ]; + return arkts.factory.createBlockExpression(statements); } } diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index be9ebdb11009ba304b153b32293d579e6feffa63..722453b7a966f09e1018ec4a2d89d0d09ede4e16 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -15,205 +15,98 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; -import { annotation, collect, filterDefined } from '../../common/arkts-utils'; import { ProjectConfig } from '../../common/plugin-context'; -import { classifyProperty, PropertyTranslator } from '../property-translators'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from '../utils'; import { - addMemoAnnotation, - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, -} from '../utils'; -import { isCustomComponentClass, isKnownMethodDefinition, isEtsGlobalClass, isReourceNode, CustomComponentScopeInfo, findCanAddMemoFromArrowFunction } from './utils'; -import { factory as uiFactory } from '../ui-factory'; + CustomComponentScopeInfo, + isResourceNode, + ScopeInfoCollection, + LoaderJson, + ResourceInfo, + loadBuildJson, + initResourceInfo, +} from './utils'; import { factory } from './factory'; import { isEntryWrapperClass } from '../entry-translators/utils'; import { factory as entryFactory } from '../entry-translators/factory'; -import { DecoratorNames, hasDecorator } from '../property-translators/utils'; -import { ScopeInfoCollection } from './utils'; - -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.AstNode[] { - const propertyMembers = propertyTranslators.map((translator) => translator.translateMember()); - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(className); - const collections = []; - if (!scope?.hasInitializeStruct) { - collections.push(factory.createInitializeStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (!scope?.hasUpdateStruct) { - collections.push(factory.createUpdateStruct(currentStructInfo, optionsTypeName, isDecl)); - } - if (currentStructInfo.isReusable) { - collections.push(factory.toRecord(optionsTypeName, currentStructInfo.toRecordBody)); - } - return collect(...collections, ...propertyMembers); -} - -function transformEtsGlobalClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - node.definition.body.map((member: arkts.AstNode) => { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - } - return member; - }); - return node; -} - -function transformOtherMembersInClass( - member: arkts.AstNode, - classTypeName: string | undefined, - classOptionsName: string | undefined, - className: string, - isDecl?: boolean -): arkts.AstNode { - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { - member.scriptFunction.setAnnotations([annotation('memo')]); - return member; - } - if ( - arkts.isMethodDefinition(member) && - isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI) && - !isDecl - ) { - return uiFactory.createConstructorMethod(member); - } - if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { - return factory.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); - } - return member; -} - -function tranformClassMembers( - node: arkts.ClassDeclaration, - isDecl?: boolean, - scope?: CustomComponentScopeInfo -): arkts.ClassDeclaration { - if (!node.definition) { - return node; - } - - let classTypeName: string | undefined; - let classOptionsName: string | undefined; - if (isDecl) { - const [classType, classOptions] = getTypeParamsFromClassDecl(node); - classTypeName = getTypeNameFromTypeParameter(classType); - classOptionsName = getTypeNameFromTypeParameter(classOptions); - } - const definition: arkts.ClassDefinition = node.definition; - const className: string | undefined = node.definition.ident?.name; - if (!className) { - throw new Error('Non Empty className expected for Component'); - } - - const propertyTranslators: PropertyTranslator[] = filterDefined( - definition.body.map((it) => classifyProperty(it, className)) - ); - const translatedMembers: arkts.AstNode[] = tranformPropertyMembers( - className, - propertyTranslators, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl, - scope - ); - const updateMembers: arkts.AstNode[] = definition.body - .filter((member) => !arkts.isClassProperty(member)) - .map((member: arkts.AstNode) => - transformOtherMembersInClass(member, classTypeName, classOptionsName, className, isDecl) - ); - - const updateClassDef: arkts.ClassDefinition = factory.updateCustomComponentClass(definition, [ - ...translatedMembers, - ...updateMembers, - ]); - return arkts.factory.updateClassDeclaration(node, updateClassDef); -} - -function transformResource( - resourceNode: arkts.CallExpression, - projectConfig: ProjectConfig | undefined -): arkts.CallExpression { - const newArgs: arkts.AstNode[] = [ - arkts.factory.create1StringLiteral(projectConfig?.bundleName ? projectConfig.bundleName : ''), - arkts.factory.create1StringLiteral(projectConfig?.moduleName ? projectConfig.moduleName : ''), - ...resourceNode.arguments, - ]; - return factory.generateTransformedResource(resourceNode, newArgs); -} +import { ImportCollector } from '../../common/import-collector'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; +import { PropertyCache } from '../property-translators/cache/propertyCache'; export class StructTransformer extends AbstractVisitor { - private scopeInfoCollection: ScopeInfoCollection; + private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { super.reset(); - this.scopeInfoCollection = { customComponents: [] }; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.push({ name: node.definition!.ident!.name }); + if (arkts.isClassDeclaration(node) && !!node.definition && node.definition.body.length > 0) { + const customComponentInfo = collectCustomComponentScopeInfo(node); + if (!!customComponentInfo) { + this.scope.customComponents.push(customComponentInfo); + } } - if (arkts.isMethodDefinition(node) && this.scopeInfoCollection.customComponents.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfoCollection.customComponents.pop()!; + const scopeInfo = this.scope.customComponents.pop()!; scopeInfo.hasInitializeStruct ||= name === CustomComponentNames.COMPONENT_INITIALIZE_STRUCT; scopeInfo.hasUpdateStruct ||= name === CustomComponentNames.COMPONENT_UPDATE_STRUCT; - scopeInfo.hasReusableRebind ||= name === CustomComponentNames.REUSABLE_COMPONENT_REBIND_STATE; - this.scopeInfoCollection.customComponents.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } - exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfoCollection.customComponents.pop(); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + this.scope.customComponents.pop(); } } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - let scope: CustomComponentScopeInfo | undefined; - const scopeInfos: CustomComponentScopeInfo[] = this.scopeInfoCollection.customComponents; - if (scopeInfos.length > 0) { - scope = scopeInfos[scopeInfos.length - 1]; - } - const newClass: arkts.ClassDeclaration = tranformClassMembers( - node, - arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), - scope - ); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + const scope: CustomComponentScopeInfo = this.scope.customComponents[this.scope.customComponents.length - 1]; + const newClass: arkts.ClassDeclaration = factory.tranformClassMembers(node, scope); this.exit(beforeChildren); return newClass; } else if (isEntryWrapperClass(node)) { entryFactory.addMemoToEntryWrapperClassMethods(node); return node; - } else if (arkts.isClassDeclaration(node) && isEtsGlobalClass(node)) { - return transformEtsGlobalClassMembers(node); - } else if (arkts.isCallExpression(node) && isReourceNode(node)) { - return transformResource(node, this.projectConfig); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); + } else if (arkts.isClassDeclaration(node)) { + return factory.transformNormalClass(node); + } else if (arkts.isCallExpression(node) && isResourceNode(node)) { + return factory.transformResource(node, this.projectConfig, this.resourceInfo); + } else if (isArkUICompatible(node)) { + return generateArkUICompatible(node as arkts.CallExpression); + } else if (arkts.isTSInterfaceDeclaration(node)) { + return factory.tranformInterfaceMembers(node, this.externalSourceName); + } + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); } return node; } diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index 2af2176268987f3a1077635768f59a00735d2bd5..1e32a07868894082c9a6d61ab206778c521dba9f 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -13,104 +13,598 @@ * limitations under the License. */ +import * as fs from 'fs'; +import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; -import { Dollars, isMemoAnnotation } from '../utils'; -import { CustomComponentNames } from '../utils'; -import { DecoratorNames, isDecoratorAnnotation } from '../property-translators/utils'; +import { CustomComponentInfo, CustomComponentNames, CustomDialogNames, isKnownMethodDefinition } from '../utils'; +import { matchPrefix } from '../../common/arkts-utils'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + Dollars, + ModuleType, + DefaultConfiguration, + LogType, + RESOURCE_TYPE, + InnerComponentNames, + ARKUI_FOREACH_SOURCE_NAME, + DecoratorNames, +} from '../../common/predefines'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { ProjectConfig } from '../../common/plugin-context'; +import { LogCollector } from '../../common/log-collector'; +import { hasDecorator } from '../../ui-plugins/property-translators/utils'; export type ScopeInfoCollection = { customComponents: CustomComponentScopeInfo[]; }; -export type CustomComponentScopeInfo = { - name: string; +export type CustomComponentScopeInfo = CustomComponentInfo & { hasInitializeStruct?: boolean; hasUpdateStruct?: boolean; hasReusableRebind?: boolean; }; +type ResourceMap = Map>; + +export interface ResourceList { + [key: string]: ResourceMap; +} + +export interface ResourceInfo { + resourcesList: ResourceList; + rawfile: Set; +} + +export interface LoaderJson { + hspResourcesMap: Record; +} + +export interface ResourceParameter { + id: number; + type: number; + params: arkts.Expression[]; +} + +export interface ObservedAnnoInfo { + className: string; + isObserved: boolean; + isObservedV2: boolean; + classHasTrace: boolean; + classHasTrack: boolean; +} + +export type ClassScopeInfo = ObservedAnnoInfo & { + getters: arkts.MethodDefinition[]; +}; + +export function getResourceParams(id: number, type: number, params: arkts.Expression[]): ResourceParameter { + return { id, type, params }; +} + /** - * Determine whether it is a custom component. + * Determine whether it is ETSGLOBAL class. * * @param node class declaration node */ -export function isCustomComponentClass(node: arkts.ClassDeclaration): boolean { - if (!node.definition?.ident?.name) { - return false; +export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { + if (node.definition?.ident?.name === 'ETSGLOBAL') { + return true; } - const name: string = node.definition.ident.name; - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - return name === CustomComponentNames.COMPONENT_CLASS_NAME || structCollection.has(name); + return false; } /** - * Determine whether it is method with specified name. + * Determine whether it is resource node begin with `$r` or `$rawfile`. * - * @param method method definition node - * @param name specified method name + * @param node call expression node */ -export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { - if (!method || !arkts.isMethodDefinition(method)) { +export function isResourceNode(node: arkts.CallExpression, ignoreDecl: boolean = false): boolean { + if ( + !( + arkts.isIdentifier(node.expression) && + (node.expression.name === Dollars.DOLLAR_RESOURCE || node.expression.name === Dollars.DOLLAR_RAWFILE) + ) + ) { return false; } + if (!ignoreDecl) { + const decl = arkts.getDecl(node.expression); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; +} - // For now, we only considered matched method name. - const isNameMatched: boolean = method.name?.name === name; - return isNameMatched; +export function isForEachCall(node: arkts.CallExpression): boolean { + if ( + arkts.isIdentifier(node.expression) && + node.expression.name === InnerComponentNames.FOR_EACH && + node.arguments.length >= 2 + ) { + return true; + } + return false; } /** - * Determine whether it is ETSGLOBAL class. + * Read the content of file 'loader.json'. * - * @param node class declaration node + * @param projectConfig configuration information of the project. */ -export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { - if (node.definition?.ident?.name === 'ETSGLOBAL') { - return true; +export function loadBuildJson(projectConfig: ProjectConfig | undefined): any { + if (!!projectConfig && projectConfig.buildLoaderJson && fs.existsSync(projectConfig.buildLoaderJson)) { + try { + const content = fs.readFileSync(projectConfig.buildLoaderJson, 'utf-8'); + const parsedContent = JSON.parse(content); + return parsedContent; + } catch (error) { + throw new Error('Error: The file is not a valid JSON format.'); + } } - return false; + return {}; } /** - * Determine whether it is resource node begin with '$r' or '$rawfile'. + * Initialize all resources information, including app resources, system resources, dependent hap resources and rawfile resources. * - * @param node call expression node + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. */ -export function isReourceNode(node: arkts.CallExpression): boolean { - if (node.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE || node.expression.dumpSrc() === Dollars.DOLLAR_RAWFILE) { - return true; +export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBuildJson: LoaderJson): ResourceInfo { + let resourcesList: ResourceList = { + app: new Map>(), + sys: new Map>(), + }; + let rawfile: Set = new Set(); + if (!!projectConfig) { + readAppResource(resourcesList, projectConfig, aceBuildJson, rawfile); } - return false; + return { resourcesList, rawfile }; } -export function isMemoCall(node: arkts.AstNode): node is arkts.CallExpression { - if (!arkts.isCallExpression(node)) { - return false; +/** + * Fill in the resource details to the resourcesList and rawfile. + * + * @param resourcesList resources including app, sys and hsp. + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param rawfile rawfile resource name set. + */ +function readAppResource( + resourcesList: ResourceList, + projectConfig: ProjectConfig, + aceBuildJson: LoaderJson, + rawfile: Set +): void { + if ('hspResourcesMap' in aceBuildJson && aceBuildJson.hspResourcesMap) { + readHspResource(aceBuildJson, projectConfig, resourcesList); + } + readSystemResource(resourcesList); + if (!!projectConfig.appResource && fs.existsSync(projectConfig.appResource)) { + const appResource: string = fs.readFileSync(projectConfig.appResource, 'utf-8'); + const resourceArr: string[] = appResource.split(/\n/); + const resourceMap: ResourceMap = new Map>(); + processResourceArr(resourceArr, resourceMap, projectConfig.appResource); + for (let [key, value] of resourceMap) { + resourcesList.app.set(key, value); + } } - const expr: arkts.AstNode = node.expression; - const decl: arkts.AstNode | undefined = arkts.getDecl(expr); + if (projectConfig.rawFileResource) { + processResourcesRawfile(projectConfig, projectConfig.rawFileResource, rawfile); + } +} - if (!decl) { - return false; +/** + * Fill in the resource details to the system resource. + * + * @param resourcesList resources including app, sys and hsp. + */ +function readSystemResource(resourcesList: ResourceList): void { + const sysResourcePath = path.resolve(__dirname, '../sysResource.js'); + if (fs.existsSync(sysResourcePath)) { + const sysObj: Record> = require(sysResourcePath).sys; + Object.keys(sysObj).forEach((key: string) => { + resourcesList.sys.set(key, sysObj[key]); + }); } +} - if (arkts.isMethodDefinition(decl)) { - return decl.scriptFunction.annotations.some( - (anno) => isDecoratorAnnotation(anno, DecoratorNames.BUILDER) || isMemoAnnotation(anno, 'memo') - ); +/** + * generate resource map. + * + * @param resourceArr lines of file 'ResourceTable.txt'. + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourcePath path of file 'ResourceTable.txt'. + */ +function processResourceArr( + resourceArr: string[], + resourceMap: Map>, + resourcePath: string +): void { + for (let i = 0; i < resourceArr.length; i++) { + if (!resourceArr[i].length) { + continue; + } + const resourceData = resourceArr[i].split(/\s/); + if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) { + rescordResourceNameAndIdMap(resourceMap, resourceData); + } else { + console.warn(`ArkTS:WARN The format of file '${resourcePath}' is incorrect.`); + break; + } + } +} + +/** + * Construct the mapping of resource type, name and id with 'ResourceTable.txt'. + * + * @param resourceMap A map that records the mapping of resource type, name and id. + * @param resourceData array of type, name and id. + */ +function rescordResourceNameAndIdMap(resourceMap: Map>, resourceData: string[]): void { + if (resourceMap.get(resourceData[0])) { + const resourceNameAndId: Record = resourceMap.get(resourceData[0])!; + if (!resourceNameAndId[resourceData[1]] || resourceNameAndId[resourceData[1]] !== Number(resourceData[2])) { + resourceNameAndId[resourceData[1]] = Number(resourceData[2]); + } + } else { + let obj: Record = {}; + obj[resourceData[1]] = Number(resourceData[2]); + resourceMap.set(resourceData[0], obj); + } +} + +/** + * Fill in the resource details to the hsp resource. + * + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + * @param resourcesList resources including app, sys and hsp. + */ +function readHspResource(aceBuildJson: LoaderJson, projectConfig: ProjectConfig, resourcesList: ResourceList): void { + projectConfig.hspResourcesMap = true; + for (const hspName in aceBuildJson.hspResourcesMap) { + if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) { + const resourceMap: ResourceMap = new Map>(); + resourcesList[hspName] = new Map>(); + const hspResource: string = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8'); + const resourceArr: string[] = hspResource.split(/\n/); + processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]); + for (const [key, value] of resourceMap) { + resourcesList[hspName].set(key, value); + } + } + } +} + +/** + * Record the information of the rawfile resource. + * + * @param projectConfig configuration information of the project. + * @param rawfilePath path of rawfile directory. + * @param rawfileSet a set includes rawfile resource names. + * @param resourceName combination of existing directory names. + */ +function processResourcesRawfile( + projectConfig: ProjectConfig, + rawfilePath: string, + rawfileSet: Set, + resourceName: string = '' +): void { + if (fs.existsSync(projectConfig.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) { + const files: string[] = fs.readdirSync(rawfilePath); + files.forEach((file: string) => { + if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) { + processResourcesRawfile( + projectConfig, + path.join(rawfilePath, file), + rawfileSet, + resourceName ? resourceName + '/' + file : file + ); + } else { + addRawfileResourceToSet(rawfileSet, file, resourceName); + } + }); + } +} + +/** + * Add rawfile name to the collection of rawfile set. + * + * @param rawfileSet a set includes rawfile resource names. + * @param file rawfile name. + * @param resourceName combination of existing directory names. + */ +function addRawfileResourceToSet(rawfileSet: Set, file: string, resourceName: string = ''): void { + if (resourceName) { + rawfileSet.add(resourceName + '/' + file); + } else { + rawfileSet.add(file); + } +} + +/** + * Verify whether the rawfile resource exists in the current module. + * + * @param resourceNode resource node. + * @param rawfileStr rawfile string. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param rawfileSet a set that records all the rawfile resources. + */ +export function checkRawfileResource( + resourceNode: arkts.CallExpression, + rawfileStr: arkts.StringLiteral, + fromOtherModule: boolean, + rawfileSet: Set +): void { + if (!fromOtherModule && !rawfileSet.has(rawfileStr.str)) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: `No such '${rawfileStr.str}' resource in current module.`, + code: '10904333', + }); } - return false; } -export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { - if (!arkts.isArrowFunctionExpression(node)) { +/** + * Check the format and the existance of resource string literal. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceData( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + let code: string | undefined; + let message: string | undefined; + if (resourceData.length !== 3) { + message = 'The input parameter is not supported.'; + code = '10905332'; + } + if (!RESOURCE_TYPE[resourceData[1]]) { + message = `The resource type ${resourceData[1]} is not supported.`; + code = '10906334'; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: LogType.ERROR, + node: resourceNode, + message: message, + code: code, + }); return false; } - const hasMemo: boolean = node.annotations.some((anno) => isMemoAnnotation(anno, 'memo')); - if (!hasMemo && !!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { - return node.scriptFunction.body.statements.some( - (st) => arkts.isExpressionStatement(st) && isMemoCall(st.expression) - ); + return preCheckResourceDataExistance(resourceNode, resourceData, resourcesList, fromOtherModule, projectConfig); +} + +/** + * Check the existance of resource string literal when the format of the string literal is correct. + * + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param literalArg string literal argument node. + * @param fromOtherModule flag about whether it is a resource for other modules. + * @param projectConfig configuration information of the project. + */ +export function preCheckResourceDataExistance( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + fromOtherModule: boolean, + projectConfig: ProjectConfig +): boolean { + if (fromOtherModule) { + if (/^\[.*\]$/.test(resourceData[0]) && projectConfig.hspResourcesMap) { + const resourceDataFirst: string = resourceData[0].replace(/^\[/, '').replace(/\]$/, '').trim(); + return resourceCheck(resourceNode, resourceData, resourcesList, true, resourceDataFirst, false); + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], true); + } + } else { + return resourceCheck(resourceNode, resourceData, resourcesList, false, resourceData[0], false); } - return false; +} + +/** + * Verify whether the app resource exists in the current module. + * + * @param resourceNode resource node. + * @param resourceData array of resource string literals. + * @param resourcesList resources including app, sys and hsp. + * @param isHarHspResourceModule flag about whether it is from hsp or har module. + * @param resourceDataFirst the first element of resource string literals. + * @param noHspResourcesMap the non-existence of hspResourcesMap. + */ +function resourceCheck( + resourceNode: arkts.CallExpression, + resourceData: string[], + resourcesList: ResourceList, + isHarHspResourceModule: boolean, + resourceDataFirst: string, + noHspResourcesMap: boolean +): boolean { + let checkResult: boolean = true; + const logType: LogType = isHarHspResourceModule ? LogType.WARN : LogType.ERROR; + let code: string | undefined; + let message: string | undefined; + if (!noHspResourcesMap && !resourcesList[resourceDataFirst]) { + code = '10903331'; + message = `Unknown resource source '${resourceDataFirst}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])) { + code = '10903330'; + message = `Unknown resource type '${resourceData[1]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } else if (!noHspResourcesMap && !resourcesList[resourceDataFirst].get(resourceData[1])![resourceData[2]]) { + code = '10903329'; + message = `Unknown resource name '${resourceData[2]}'.`; + checkResult = isHarHspResourceModule ? checkResult : false; + } + if (!!code && !!message) { + LogCollector.getInstance().collectLogInfo({ + type: logType, + node: resourceNode, + message: message, + code: code, + }); + } + return checkResult; +} + +/** + * generate bundleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceBundleName(projectConfig: ProjectConfig, isDynamicBundleOrModule: boolean): string { + if (projectConfig.resetBundleName || projectConfig.allowEmptyBundleName) { + return ''; + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_BUNDLE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_BUNDLE_NAME + : projectConfig.bundleName + ? projectConfig.bundleName + : ''; +} + +/** + * generate moduleName for $r and $rawfile. + * + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. + */ +export function generateResourceModuleName( + projectConfig: ProjectConfig, + isDynamicBundleOrModule: boolean = false, + resourceModuleName: string, + fromOtherModule: boolean +): string { + if (fromOtherModule && resourceModuleName) { + return resourceModuleName.replace(/^\[|\]$/g, ''); + } + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_MODULE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_MODULE_NAME + : projectConfig.moduleName + ? projectConfig.moduleName + : ''; +} + +/** + * Determine whether to use dynamic module name and bundle name. + * + * @param projectConfig project config. + */ +export function isDynamicName(projectConfig: ProjectConfig): boolean { + const isByteCodeHar: boolean = projectConfig.moduleType === ModuleType.HAR && projectConfig.byteCodeHar; + const uiTransformOptimization: boolean = !!projectConfig.uiTransformOptimization; + return uiTransformOptimization ? uiTransformOptimization : isByteCodeHar; +} + +/** + * Determine whether the node is ForEach method declaration. + * + * @param node method definition node. + * @param sourceName external source name. + */ +export function isForEachDecl(node: arkts.MethodDefinition, sourceName: string | undefined): boolean { + const isForEach: boolean = !!node.name && node.name.name === InnerComponentNames.FOR_EACH; + const isMethodDecl: boolean = + !!node.scriptFunction && + arkts.hasModifierFlag(node.scriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + return isForEach && isMethodDecl && !!sourceName && sourceName === ARKUI_FOREACH_SOURCE_NAME; +} + +export function getCustomDialogController(node: arkts.ClassProperty): string { + const key = node.key; + let controllerName: string = ''; + if (!(key && arkts.isIdentifier(key) && node.typeAnnotation)) { + return ''; + } + const typeAnno = node.typeAnnotation; + if (arkts.isETSUnionType(typeAnno)) { + for (const type of typeAnno.types) { + if (arkts.isETSTypeReference(type)) { + controllerName = hasController(type, key.name); + } + } + } else if (arkts.isETSTypeReference(typeAnno)) { + controllerName = hasController(typeAnno, key.name); + } + return controllerName; +} + +export function hasController(node: arkts.ETSTypeReference, keyName: string): string { + const ident = node.part?.name; + if (ident && arkts.isIdentifier(ident) && ident.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER) { + return keyName; + } + return ''; +} + +export function isInvalidDialogControllerOptions(args: readonly arkts.Expression[]): boolean { + const firstOptionsParameter: arkts.AstNode = args[0]; + return ( + args.length <= 0 || + !(isObjectAsExpression(firstOptionsParameter) || arkts.isObjectExpression(firstOptionsParameter)) + ); +} + +function isObjectAsExpression(param: arkts.AstNode): boolean { + return arkts.isTSAsExpression(param) && !!param.expr && arkts.isObjectExpression(param.expr); +} + +export function isComputedMethod(node: arkts.AstNode): boolean { + return arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.COMPUTED); +} + +export function findBuilderIndexInControllerOptions(properties: readonly arkts.Expression[]): number { + return properties.findIndex((item: arkts.Expression) => { + return ( + arkts.isProperty(item) && + !!item.key && + arkts.isIdentifier(item.key) && + item.key.name === CustomDialogNames.OPTIONS_BUILDER + ); + }); +} + +export function getNoTransformationMembersInClass( + definition: arkts.ClassDefinition, + ObservedAnno: ObservedAnnoInfo +): arkts.AstNode[] { + return definition.body.filter( + (member) => + !arkts.isClassProperty(member) && + !( + arkts.isMethodDefinition(member) && + arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) + ) && + !( + ObservedAnno.isObservedV2 && + arkts.isMethodDefinition(member) && + (hasDecorator(member, DecoratorNames.COMPUTED) || + isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_CONSTRUCTOR_ORI)) + ) + ); } diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index 2b64ac64fd6c7775c6830c58f2e50aa541cb6d32..8b7280f0d1ed34a377f7a6cea0e0bb900ea8c065 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -14,9 +14,44 @@ */ import * as arkts from '@koalaui/libarkts'; -import { BuilderLambdaNames, CustomComponentNames, hasPropertyInAnnotation, hasNullOrUndefinedType } from './utils'; -import { annotation } from '../common/arkts-utils'; -import { DecoratorNames, needDefiniteOrOptionalModifier } from './property-translators/utils'; +import { + BuilderLambdaNames, + CustomComponentAnontations, + CustomComponentNames, + CustomDialogNames, + hasNullOrUndefinedType, + hasPropertyInAnnotation, +} from './utils'; +import { PartialExcept, PartialNested, PartialNestedExcept } from '../common/safe-types'; +import { ArkTsDefaultNames, DecoratorNames } from '../common/predefines'; +import { needDefiniteOrOptionalModifier } from './property-translators/utils'; +import { addMemoAnnotation } from '../collectors/memo-collectors/utils'; + +export interface ScriptFunctionConfiguration { + key: arkts.Identifier | undefined; + body: arkts.AstNode | undefined; + typeParams: arkts.TSTypeParameterDeclaration | undefined; + params: readonly arkts.Expression[]; + returnTypeAnnotation: arkts.TypeNode | undefined; + hasReceiver: boolean; + flags: arkts.Es2pandaScriptFunctionFlags; + modifiers: arkts.Es2pandaModifierFlags; + annotations: arkts.AnnotationUsage[]; +} + +export interface MethodDefinitionConfiguration { + key: arkts.Identifier; + kind: arkts.Es2pandaMethodDefinitionKind; + function: ScriptFunctionConfiguration; + overloads: arkts.MethodDefinition[]; + modifiers: arkts.Es2pandaModifierFlags; + isComputed: boolean; +} + +export interface IntrinsicAnnotationDeclarationConfiguration { + expr: arkts.Identifier; + properties: arkts.AstNode[]; +} export class factory { /** @@ -64,16 +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.factory.createParameterDeclaration(styleParam, undefined); - param.annotations = [annotation('memo')]; - return param; - } - /** * create `initializers: | undefined` as identifier */ @@ -112,8 +137,8 @@ export class factory { */ static createContentParameter(): arkts.ETSParameterExpression { const contentParam: arkts.Identifier = factory.createContentIdentifier(); - const param = arkts.factory.createParameterDeclaration(contentParam, undefined); - param.annotations = [annotation('memo')]; + const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration(contentParam, undefined); + addMemoAnnotation(param); return param; } @@ -126,6 +151,18 @@ export class factory { ); } + /** + * create complex type from string and type parameter, e.g. `Set` + */ + static createComplexTypeFromStringAndTypeParameter(name: string, params: readonly arkts.TypeNode[]): arkts.TypeNode { + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(name), + arkts.factory.createTSTypeParameterInstantiation(params) + ) + ); + } + /** * create `() => `. If returnType is not given, then using `void`. */ @@ -145,27 +182,6 @@ export class factory { } /** - * create and insert `import { as } from ` to the top of script's statements. - */ - static createAndInsertImportDeclaration( - source: arkts.StringLiteral, - imported: arkts.Identifier, - local: arkts.Identifier, - importKind: arkts.Es2pandaImportKinds, - program: arkts.Program - ): void { - const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( - source, - [arkts.factory.createImportSpecifier(imported, local)], - importKind, - program, - arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE - ); - arkts.importDeclarationInsert(importDecl, program); - return; - } - - /* * create `import { as } ...`. */ static createAdditionalImportSpecifier(imported: string, local: string): arkts.ImportSpecifier { @@ -175,62 +191,129 @@ export class factory { ); } - /* - * create `constructor() {}`. + /** + * update ScriptFunction with configurations. */ - static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, - member.name, - member.scriptFunction, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, - false + static updateScriptFunction( + original: arkts.ScriptFunction, + config: Partial + ): arkts.ScriptFunction { + const newFunc: arkts.ScriptFunction = arkts.factory.updateScriptFunction( + original, + config.body ?? original.body, + arkts.factory.createFunctionSignature( + config.typeParams ?? original.typeParams, + config.params ?? original.params, + config.returnTypeAnnotation ?? original.returnTypeAnnotation, + config.hasReceiver ?? original.hasReceiver + ), + config.flags ?? original.flags, + config.modifiers ?? original.modifiers + ); + if (!!config.key) { + newFunc.setIdent(config.key); + } + if (!!config.annotations) { + newFunc.setAnnotations(config.annotations); + } + return newFunc; + } + + /** + * create ScriptFunction with configurations. + */ + static createScriptFunction(config: Partial): arkts.ScriptFunction { + const newFunc: arkts.ScriptFunction = arkts.factory.createScriptFunction( + config.body ?? undefined, + arkts.factory.createFunctionSignature( + config.typeParams ?? undefined, + config.params ?? [], + config.returnTypeAnnotation ?? undefined, + config.hasReceiver ?? false + ), + config.flags ?? arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_NONE, + config.modifiers ?? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE ); + if (!!config.key) { + newFunc.setIdent(config.key); + } + if (!!config.annotations) { + newFunc.setAnnotations(config.annotations); + } + return newFunc; } - /* - * create `@memo() _build(<>)`. + /** + * update MethodDefinition with configurations. */ - static transformBuildMethodWithOriginBuild( - method: arkts.MethodDefinition, - typeName: string, - optionsName: string, - isDecl?: boolean + static updateMethodDefinition( + original: arkts.MethodDefinition, + config: PartialNested ): 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, - [ - factory.createStyleParameter(typeName), - factory.createContentParameter(), - factory.createInitializersOptionsParameter(optionsName), - ], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + const key: arkts.Identifier = config.key ?? original.name; + const newFunc: arkts.ScriptFunction = factory.updateScriptFunction(original.scriptFunction, { + ...config.function, + key, + }); + const newMethod: arkts.MethodDefinition = arkts.factory.updateMethodDefinition( + original, + config.kind ?? original.kind, + key, + newFunc, + config.modifiers ?? original.modifiers, + config.isComputed ?? false + ); + if (!!config.overloads) { + newMethod.setOverloads(config.overloads); + } + return newMethod; + } + + /** + * create MethodDefinition with configurations. + */ + static createMethodDefinition(config: PartialNested): arkts.MethodDefinition { + const newFunc: arkts.ScriptFunction = factory.createScriptFunction({ + ...config.function, + key: config.key, + }); + const newMethod: arkts.MethodDefinition = arkts.factory.createMethodDefinition( + config.kind ?? arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_NONE, + config.key!, + newFunc, + config.modifiers ?? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + config.isComputed ?? false + ); + if (!!config.overloads) { + newMethod.setOverloads(config.overloads); + } + return newMethod; + } + + /** + * create intrinsic `@Retention({policy:"SOURCE"})` AnnotationDeclaration with configurations. + */ + static createIntrinsicAnnotationDeclaration( + config: PartialExcept + ): arkts.AnnotationDeclaration { + const intrinsicAnnotations: arkts.AnnotationUsage[] = [ + arkts.factory.create1AnnotationUsage(arkts.factory.createIdentifier('Retention'), [ + arkts.factory.createClassProperty( + arkts.factory.createIdentifier('policy'), + arkts.factory.createStringLiteral('SOURCE'), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ), - scriptFunction.flags, - scriptFunction.modifiers - ) - .setAnnotations([annotation('memo')]); - - 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 - ); + ]), + ]; + const newAnnotationDecl: arkts.AnnotationDeclaration = arkts.factory + .createAnnotationDeclaration(config.expr, config.properties ?? []) + .setAnnotations(intrinsicAnnotations); + return newAnnotationDecl; } - /* + /** * add alias: to @Provide annotation when no alias in @Provide({...}). */ static processNoAliasProvideVariable(property: arkts.ClassProperty): void { @@ -258,7 +341,7 @@ export class factory { property.setAnnotations(newAnnos); } - /* + /** * create class property : `alias: `. */ static createAliasClassProperty(value: arkts.Identifier): arkts.ClassProperty { @@ -271,17 +354,138 @@ export class factory { ); } - /* + /** * add optional or definite modifier for class property needs initializing without assignment. */ - static PreprocessClassPropertyModifier(st: arkts.AstNode): arkts.AstNode { - if (arkts.isClassProperty(st) && needDefiniteOrOptionalModifier(st)) { + static PreprocessClassPropertyModifier(st: arkts.AstNode, isDecl: boolean): arkts.AstNode { + if (!isDecl && arkts.isClassProperty(st) && needDefiniteOrOptionalModifier(st)) { if (st.typeAnnotation && hasNullOrUndefinedType(st.typeAnnotation)) { - st.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; + st.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL; } else { - st.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DEFINITE; + st.modifiers |= arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DEFINITE; } } 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; + } + + /** + * create class property node: `:`. + * + * @param method method definition node + */ + static createPropertyInInterface(key: string, type?: arkts.TypeNode): arkts.ClassProperty { + const keyIdent: arkts.Identifier = arkts.factory.createIdentifier(key); + return arkts.factory.createClassProperty( + keyIdent, + undefined, + type, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL, + false + ); + } + + /** + * add `baseComponent: ExtendableComponent` to interface CustomDialogControllerOptions. + * + * @param method method definition node + */ + static updateCustomDialogOptionsInterface(newNode: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + if (!newNode.body?.body || newNode.body?.body.length <= 0) { + return newNode; + } + + return arkts.factory.updateInterfaceDeclaration( + newNode, + newNode.extends, + newNode.id, + newNode.typeParams, + arkts.factory.updateInterfaceBody(newNode.body!, [ + ...newNode.body.body, + factory.createPropertyInInterface( + CustomDialogNames.BASE_COMPONENT, + factory.createTypeReferenceFromString(CustomDialogNames.EXTENDABLE_COMPONENT) + ), + ]), + newNode.isStatic, + newNode.isFromExternal + ); + } + + /** + * Generate member expression, e.g. `.`. + * + * @param method method definition node + */ + static generateMemberExpression(object: arkts.AstNode, property: string, optional = false): arkts.MemberExpression { + return arkts.factory.createMemberExpression( + object, + arkts.factory.createIdentifier(property), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + optional + ); + } + + /** + * create `: = ` as parameter + */ + static createParameterDeclaration( + keyName: string, + typeName: string, + initializers?: arkts.AstNode + ): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(keyName, this.createTypeReferenceFromString(typeName)), + initializers + ); + } + + /** + * create class static block, e.g. `static {}`. + */ + static createClassStaticBlock(): arkts.ClassStaticBlock { + return arkts.factory.createClassStaticBlock( + arkts.factory.createFunctionExpression( + factory.createScriptFunction({ + key: arkts.factory.createIdentifier(ArkTsDefaultNames.DEFAULT_STATIC_BLOCK_NAME), + body: arkts.factory.createBlock([]), + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + flags: + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_STATIC_BLOCK | + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_EXPRESSION, + }) + ) + ); + } } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index 847a830a55d073882303beadbe04b4b210dbabba..210a2aeabfcb7278c9972cb004ae289e2e3334ae 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -14,25 +14,42 @@ */ import * as arkts from '@koalaui/libarkts'; -import { annotation } from '../common/arkts-utils'; -import { DecoratorNames } from './property-translators/utils'; +import { matchPrefix } from '../common/arkts-utils'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME, + DecoratorNames, + StructDecoratorNames, +} from '../common/predefines'; +import { DeclarationCollector } from '../common/declaration-collector'; +import { hasDecorator } from './property-translators/utils'; export enum CustomComponentNames { - ENTRY_ANNOTATION_NAME = 'Entry', - COMPONENT_ANNOTATION_NAME = 'Component', - RESUABLE_ANNOTATION_NAME = 'Reusable', COMPONENT_BUILD_ORI = 'build', COMPONENT_CONSTRUCTOR_ORI = 'constructor', - COMPONENT_DEFAULT_IMPORT = 'arkui.component.customComponent', COMPONENT_CLASS_NAME = 'CustomComponent', + COMPONENT_V2_CLASS_NAME = 'CustomComponentV2', + BASE_CUSTOM_DIALOG_NAME = 'BaseCustomDialog', COMPONENT_INTERFACE_PREFIX = '__Options_', COMPONENT_INITIALIZE_STRUCT = '__initializeStruct', COMPONENT_UPDATE_STRUCT = '__updateStruct', - COMPONENT_BUILD = '_build', - REUSABLE_COMPONENT_REBIND_STATE = '__rebindStates', COMPONENT_INITIALIZERS_NAME = 'initializers', BUILDCOMPATIBLENODE = '_buildCompatibleNode', OPTIONS = 'options', + PAGE_LIFE_CYCLE = 'PageLifeCycle', + LAYOUT_CALLBACK = 'LayoutCallback', +} + +export enum CustomDialogNames { + CUSTOM_DIALOG_ANNOTATION_NAME = 'CustomDialog', + CUSTOM_DIALOG_CONTROLLER = 'CustomDialogController', + CUSTOM_DIALOG_CONTROLLER_OPTIONS = 'CustomDialogControllerOptions', + SET_DIALOG_CONTROLLER_METHOD = '__setDialogController__', + CONTROLLER = 'controller', + OPTIONS_BUILDER = 'builder', + BASE_COMPONENT = 'baseComponent', + EXTENDABLE_COMPONENT = 'ExtendableComponent', + CUSTOM_BUILDER = 'CustomBuilder', } export enum BuilderLambdaNames { @@ -42,15 +59,23 @@ export enum BuilderLambdaNames { STYLE_PARAM_NAME = 'style', STYLE_ARROW_PARAM_NAME = 'instance', CONTENT_PARAM_NAME = 'content', - ANIMATION_NAME = 'animation', - ANIMATION_START = 'animationStart', - ANIMATION_STOP = 'animationStop', } -export enum Dollars { - DOLLAR_RESOURCE = '$r', - DOLLAR_RAWFILE = '$rawfile', - DOLLAR_DOLLAR = '$$', +// IMPORT +export function findImportSourceByName(importName: string): string { + const source = DeclarationCollector.getInstance().findExternalSourceFromName(importName); + if (!source) { + throw new Error(`cannot find import source by name: "${importName}".`); + } + return source; +} + +export function findImportSourceByNode(declNode: arkts.AstNode): string { + const source = DeclarationCollector.getInstance().findExternalSourceFromNode(declNode); + if (!source) { + throw new Error(`cannot find import source by peer.`); + } + return source; } export function findLocalImport( @@ -67,22 +92,30 @@ export function findLocalImport( return importSpecifier?.local ?? importSpecifier?.imported; } -// TODO: currently, we forcely assume initializerOptions is named in pattern __Options_xxx -export function getCustomComponentNameFromInitializerOptions(name: string): string | undefined { - const prefix: string = CustomComponentNames.COMPONENT_INTERFACE_PREFIX; - if (name.startsWith(prefix)) { - return name.substring(prefix.length); - } -} - -export function getCustomComponentOptionsName(className: string): string { - return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; -} - +// AST NODE export function isStatic(node: arkts.AstNode): boolean { return node.isStatic; } +/** + * Determine whether the type node includes null or undefined type. + * + * @param type type node + */ +export function hasNullOrUndefinedType(type: arkts.TypeNode): boolean { + let res: boolean = false; + if (arkts.isETSUnionType(type)) { + type.types.forEach((item: arkts.TypeNode) => { + res = res || hasNullOrUndefinedType(item); + }); + } + if (arkts.isETSUndefinedType(type) || arkts.isETSNullType(type)) { + res = true; + } + return res; +} + +// TYPE PARAMETER export function getTypeParamsFromClassDecl(node: arkts.ClassDeclaration | undefined): readonly arkts.TSTypeParameter[] { return node?.definition?.typeParams?.params ?? []; } @@ -91,109 +124,264 @@ export function getTypeNameFromTypeParameter(node: arkts.TSTypeParameter | undef return node?.name?.name; } -export function createOptionalClassProperty( - name: string, - property: arkts.ClassProperty, - stageManagementIdent: string, - modifiers: arkts.Es2pandaModifierFlags, - needMemo: boolean = false -): arkts.ClassProperty { - const newType: arkts.TypeNode | undefined = property.typeAnnotation?.clone(); - if (needMemo) { - newType?.setAnnotations([annotation('memo')]); - } - const newProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier(name), - undefined, - stageManagementIdent.length ? createStageManagementType(stageManagementIdent, property) : newType, - modifiers, - false - ); - return arkts.classPropertySetOptional(newProperty, true); -} - -export function createStageManagementType( - stageManagementIdent: string, - property: arkts.ClassProperty -): arkts.ETSTypeReference { - return arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier(stageManagementIdent), - arkts.factory.createTSTypeParameterInstantiation([ - property.typeAnnotation ? property.typeAnnotation.clone() : arkts.factory.createETSUndefinedType(), - ]) - ) - ); -} - +// GETTER export function getGettersFromClassDecl(definition: arkts.ClassDefinition): arkts.MethodDefinition[] { return definition.body.filter( (member) => arkts.isMethodDefinition(member) && - arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) + arkts.hasModifierFlag(member, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER) && + !hasDecorator(member, DecoratorNames.COMPUTED) ) as arkts.MethodDefinition[]; } -export type MemoAstNode = - | arkts.ScriptFunction - | arkts.ETSParameterExpression - | arkts.ClassProperty - | arkts.TSTypeAliasDeclaration - | arkts.ETSFunctionType - | arkts.ArrowFunctionExpression - | arkts.ETSUnionType; +// ANNOTATION +export function hasPropertyInAnnotation(annotation: arkts.AnnotationUsage, propertyName: string): boolean { + return !!annotation.properties.find( + (annoProp: arkts.AstNode) => + arkts.isClassProperty(annoProp) && + annoProp.key && + arkts.isIdentifier(annoProp.key) && + annoProp.key.name === propertyName + ); +} + +// CUSTOM COMPONENT +export type CustomComponentInfo = { + name: string; + isDecl: boolean; + annotations: CustomComponentAnontations; +}; + +export type CustomComponentAnontations = { + component?: arkts.AnnotationUsage; + componentV2?: arkts.AnnotationUsage; + entry?: arkts.AnnotationUsage; + reusable?: arkts.AnnotationUsage; + reusableV2?: arkts.AnnotationUsage; + customLayout?: arkts.AnnotationUsage; + customdialog?: arkts.AnnotationUsage; +}; + +type StructAnnoationInfo = { + isComponent: boolean; + isComponentV2: boolean; + isEntry: boolean; + isReusable: boolean; + isReusableV2: boolean; + isCustomLayout: boolean; + isCustomDialog: boolean; +}; + +export type ComponentType = { + hasComponent: boolean; + hasComponentV2: boolean; + hasCustomDialog: boolean; + hasCustomLayout: boolean; +}; + +export type ClassInfo = { + className: string; + isFromStruct: boolean; +}; -export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: string): boolean { - if (!(node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName)) { +export function isCustomComponentAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: StructDecoratorNames, + ignoreDecl?: boolean +): boolean { + if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { return false; } + if (!ignoreDecl) { + const decl = arkts.getDecl(anno.expr); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } return true; } -export function addMemoAnnotation(node: T, memoName: string = 'memo'): T { - if (arkts.isETSUnionType(node)) { - const functionType = node.types.find((type) => arkts.isETSFunctionType(type)); - if (!functionType) { - return node; +export function collectCustomComponentScopeInfo( + node: arkts.ClassDeclaration | arkts.StructDeclaration +): CustomComponentInfo | undefined { + const definition: arkts.ClassDefinition | undefined = node.definition; + if (!definition || !definition?.ident?.name) { + return undefined; + } + const isStruct = arkts.isStructDeclaration(node); + 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 && + definition.ident.name !== CustomComponentNames.COMPONENT_V2_CLASS_NAME && + definition.ident.name !== CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ) { + return undefined; + } + let annotations: CustomComponentAnontations = {}; + if (!isCustomComponentClassDecl) { + let isCustomComponent: boolean = false; + for (const anno of definition.annotations) { + const { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout, isCustomDialog } = + getAnnotationInfoForStruct(anno, shouldIgnoreDecl); + isCustomComponent ||= isComponent || isComponentV2 || isCustomDialog; + 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 }), + ...(isCustomDialog && !annotations?.customdialog && { customdialog: anno }), + }; + } + if (!isCustomComponent) { + return undefined; } - addMemoAnnotation(functionType, memoName); - return node; } - const newAnnotations: arkts.AnnotationUsage[] = [ - ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), - annotation(memoName), - ]; - if (arkts.isEtsParameterExpression(node)) { - node.annotations = newAnnotations; - return node; + return { + name: definition.ident.name, + isDecl, + annotations: annotations as CustomComponentAnontations, + }; +} + +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); + const isCustomDialog = isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOMDIALOG, shouldIgnoreDecl); + return { isComponent, isComponentV2, isEntry, isReusable, isReusableV2, isCustomLayout, isCustomDialog }; +} + +export function isComponentStruct(node: arkts.StructDeclaration, scopeInfo: CustomComponentInfo): boolean { + return scopeInfo.name === node.definition.ident?.name; +} + +/** + * Determine whether it is a custom component. + * + * @param node class declaration node + */ +export function isCustomComponentClass(node: arkts.ClassDeclaration, scopeInfo: CustomComponentInfo): boolean { + if (!node.definition?.ident?.name) { + return false; + } + const name: string = node.definition.ident.name; + if (scopeInfo.isDecl) { + return ( + name === CustomComponentNames.COMPONENT_CLASS_NAME || + name === CustomComponentNames.COMPONENT_V2_CLASS_NAME || + name === CustomComponentNames.BASE_CUSTOM_DIALOG_NAME + ); } - return node.setAnnotations(newAnnotations) as T; + return name === scopeInfo.name; } -export function hasPropertyInAnnotation(annotation: arkts.AnnotationUsage, propertyName: string): boolean { - return !!annotation.properties.find( - (annoProp: arkts.AstNode) => - arkts.isClassProperty(annoProp) && - annoProp.key && - arkts.isIdentifier(annoProp.key) && - annoProp.key.name === propertyName +export function isCustomComponentInterface(node: arkts.TSInterfaceDeclaration): boolean { + const checkPrefix = !!node.id?.name.startsWith(CustomComponentNames.COMPONENT_INTERFACE_PREFIX); + const checkComponent = node.annotations.some( + (anno) => + isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT) || + isCustomComponentAnnotation(anno, StructDecoratorNames.COMPONENT_V2) || + isCustomComponentAnnotation(anno, StructDecoratorNames.CUSTOMDIALOG) ); + return checkPrefix && checkComponent; +} + +export function getCustomComponentOptionsName(className: string): string { + return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; } /** - * Determine whether the type node includes null or undefined type. + * Determine whether it is method with specified name. * - * @param type type node + * @param method method definition node + * @param name specified method name */ -export function hasNullOrUndefinedType(type: arkts.TypeNode): boolean { - let res: boolean = false; - if (arkts.isETSUnionType(type)) { - type.types.forEach((item: arkts.TypeNode) => { - res = res || hasNullOrUndefinedType(item); - }); +export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { + if (!method || !arkts.isMethodDefinition(method)) { + return false; } - if (arkts.isETSUndefinedType(type) || arkts.isETSNullType(type)) { - res = true; + + // For now, we only considered matched method name. + const isNameMatched: boolean = method.name?.name === name; + return isNameMatched; +} + +export function isSpecificNewClass(node: arkts.ETSNewClassInstanceExpression, className: string): boolean { + if ( + node.getTypeRef && + arkts.isETSTypeReference(node.getTypeRef) && + node.getTypeRef.part && + arkts.isETSTypeReferencePart(node.getTypeRef.part) && + node.getTypeRef.part.name && + arkts.isIdentifier(node.getTypeRef.part.name) && + node.getTypeRef.part.name.name === className + ) { + return true; } - return res; -} \ No newline at end of file + return false; +} + +export function isCustomDialogControllerOptions( + newNode: arkts.TSInterfaceDeclaration, + externalSourceName: string | undefined +): boolean { + return ( + externalSourceName === CUSTOM_DIALOG_CONTROLLER_SOURCE_NAME && + !!newNode.id && + newNode.id.name === CustomDialogNames.CUSTOM_DIALOG_CONTROLLER_OPTIONS + ); +} + +export function getComponentExtendsName(annotations: CustomComponentAnontations, componentType: ComponentType): string { + if (!!annotations.customLayout) { + componentType.hasCustomLayout ||= true; + } + if (!!annotations.customdialog) { + componentType.hasCustomDialog ||= true; + return CustomComponentNames.BASE_CUSTOM_DIALOG_NAME; + } + if (!!annotations.componentV2) { + componentType.hasComponentV2 ||= true; + return CustomComponentNames.COMPONENT_V2_CLASS_NAME; + } + componentType.hasComponent ||= true; + return CustomComponentNames.COMPONENT_CLASS_NAME; +} + +export function computedField(name: string): string { + return `__computed_${name}`; +} + +export function monitorField(name: string): string { + return `__monitor_${name}`; +} + +export function getClassPropertyType(property: arkts.ClassProperty): arkts.TypeNode | undefined { + const type = property.typeAnnotation; + if (!!type) { + return type.clone(); + } + let value: arkts.Expression | undefined = property.value; + let inferredType: arkts.AstNode | undefined = value ? arkts.createTypeNodeFromTsType(value) : undefined; + if (inferredType && arkts.isTypeNode(inferredType)) { + return inferredType; + } + return undefined; +} diff --git a/arkui-plugins/ui-syntax-plugins/index.ts b/arkui-plugins/ui-syntax-plugins/index.ts index 53232081ede8d769c434e28a3bbb0b038e6a304f..c32e154c1e5da7f423b942ba8166335e654d25ee 100644 --- a/arkui-plugins/ui-syntax-plugins/index.ts +++ b/arkui-plugins/ui-syntax-plugins/index.ts @@ -14,31 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PluginContext, Plugins } from '../common/plugin-context'; -import { ParsedUISyntaxLinterTransformer } from './transformers/parsed-ui-syntax-linter-transformer'; -import { createUISyntaxRuleProcessor } from './processor'; +import { PluginContext, PluginHandler, Plugins } from '../common/plugin-context'; +import { + CheckedUISyntaxLinterTransformer, + ParsedUISyntaxLinterTransformer, +} from './transformers/ui-syntax-linter-transformer'; +import { createUISyntaxRuleProcessor, UISyntaxRuleProcessor } from './processor'; +import { UISyntaxLinterVisitor } from './transformers/ui-syntax-linter-visitor'; import rules from './rules'; +import { matchPrefix } from '../common/arkts-utils'; +import { EXCLUDE_EXTERNAL_SOURCE_PREFIXES, tracePerformance } from './utils'; export function uiSyntaxLinterTransform(): Plugins { - const processor = createUISyntaxRuleProcessor(rules); - return { - name: 'ui-syntax-plugin', - parsed(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); - if (!contextPtr) { + const processor = createUISyntaxRuleProcessor(rules); + const parsedTransformer = new ParsedUISyntaxLinterTransformer(processor); + const checkedTransformer = new CheckedUISyntaxLinterTransformer(processor); + return { + name: 'ui-syntax-plugin', + parsed: createTransformer('parsed', processor, parsedTransformer), + checked: createTransformer('checked', processor, checkedTransformer), + }; +} + +function createTransformer( + phase: string, + processor: UISyntaxRuleProcessor, + transformer: UISyntaxLinterVisitor +): PluginHandler { + const visitedPrograms: Set = new Set(); + const visitedExternalSources: Set = new Set(); + return tracePerformance(`UISyntaxPlugin::${phase}`, function (this: PluginContext): arkts.EtsScript | undefined { + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!contextPtr) { + return undefined; + } + const projectConfig = this.getProjectConfig(); + if (!projectConfig) { + return undefined; + } + processor.setProjectConfig(projectConfig); + if (projectConfig.frameworkMode) { + return undefined; + } + const program = arkts.getOrUpdateGlobalContext(contextPtr).program; + if (visitedPrograms.has(program.peer) || isHeaderFile(program.absName)) { + return undefined; + } + const isCoding = this.isCoding?.() ?? false; + if (isCoding) { + const codingFilePath = this.getCodingFilePath(); + if (program.absName === codingFilePath) { + return transformProgram.call(this, transformer, program); + } + } else { + transformExternalSources.call(this, program, visitedExternalSources, visitedPrograms, transformer); + if (program.absName) { + return transformProgram.call(this, transformer, program); + } + } + visitedPrograms.add(program.peer); return undefined; - } - let program = arkts.getOrUpdateGlobalContext(contextPtr).program; - const node = program.astNode; - if (node) { - const script = new ParsedUISyntaxLinterTransformer(processor).visitor( - node, - ) as arkts.EtsScript; - arkts.setAllParents(script); - this.setArkTSAst(script); - return script; - } - return undefined; + }); +} + +function transformExternalSources( + this: PluginContext, + program: arkts.Program, + visitedExternalSources: Set, + visitedPrograms: Set, + transformer: UISyntaxLinterVisitor +): void { + const externalSources = program.externalSources; + for (const externalSource of externalSources) { + if (matchPrefix(EXCLUDE_EXTERNAL_SOURCE_PREFIXES, externalSource.getName())) { + continue; + } + if (visitedExternalSources.has(externalSource)) { + continue; + } + const programs = externalSource.programs; + for (const program of programs) { + if (visitedPrograms.has(program.peer) || isHeaderFile(program.absName)) { + continue; + } + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); + } + visitedExternalSources.add(externalSource.peer); } - }; +} + +function transformProgram( + this: PluginContext, + transformer: UISyntaxLinterVisitor, + program: arkts.Program +): arkts.EtsScript { + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); + return script; +} + +function isHeaderFile(fileName: string): boolean { + return fileName.endsWith('.d.ets'); } diff --git a/arkui-plugins/ui-syntax-plugins/processor/index.ts b/arkui-plugins/ui-syntax-plugins/processor/index.ts index 0a447759e85e1d8c7e5eaa0698820bc082ea52a7..6aa704560719765f9d2cd932ad038a099640a266 100644 --- a/arkui-plugins/ui-syntax-plugins/processor/index.ts +++ b/arkui-plugins/ui-syntax-plugins/processor/index.ts @@ -14,51 +14,177 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from '../rules/ui-syntax-rule'; -import { getContainerComponents } from '../utils'; +import * as path from 'node:path'; +import { + AbstractUISyntaxRule, + ReportOptions, + UISyntaxRule, + UISyntaxRuleConfig, + UISyntaxRuleContext, + UISyntaxRuleHandler, +} from '../rules/ui-syntax-rule'; +import { getUIComponents, readJSON, UISyntaxRuleComponents } from '../utils'; +import { ProjectConfig } from 'common/plugin-context'; export type UISyntaxRuleProcessor = { - parsed(node: arkts.AstNode): void; + setProjectConfig(projectConfig: ProjectConfig): void; + beforeTransform(): void; + afterTransform(): void; + parsed(node: arkts.AstNode): void; + checked(node: arkts.AstNode): void; }; -export function createUISyntaxRuleProcessor( - rules: UISyntaxRule[], -): UISyntaxRuleProcessor { - const containerComponents = getContainerComponents('../../components/'); - const context: UISyntaxRuleContext = { - report(options) { - const position = arkts.getStartPosition(options.node); - let message: string; - if (!options.data) { - message = options.message; - } else { - message = Object.entries(options.data).reduce( - (message, [placehoderName, placehoderValue]) => { - return message.replace(`{{${placehoderName}}}`, placehoderValue); - }, - options.message, +type ModuleConfig = { + module: { + pages: string; + }; +}; + +type MainPages = { + src: string[]; +}; + +const BASE_RESOURCE_PATH = 'src/main/resources/base'; +const ETS_PATH = 'src/main/ets'; + +class ConcreteUISyntaxRuleContext implements UISyntaxRuleContext { + public componentsInfo: UISyntaxRuleComponents | undefined; + public projectConfig?: ProjectConfig; + + constructor() { + this.componentsInfo = getUIComponents('../../components/'); + } + + public report(options: ReportOptions): void { + let message: string; + if (!options.data) { + message = options.message; + } else { + message = this.format(options.message, options.data); + } + + const diagnosticKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + options.level === 'error' + ? arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR + : arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_WARNING ); - } - // todo - if (options.fix) { - const suggestion = options.fix(options.node); - console.log(`error: ${message}`); - console.log(`range: (${suggestion.range[0].index()}, ${suggestion.range[0].line()}) - (${suggestion.range[1].index()}, ${suggestion.range[1].line()})`, - `code: ${suggestion.code}`); - } else { - console.log(`syntax-error: ${message} (${position.index()},${position.line()})`); - } - }, - containerComponents: containerComponents, - }; - - const instances = rules.map((rule) => rule.setup(context)); - - return { - parsed(node): void { - for (const instance of instances) { - instance.parsed?.(node); - } - }, - }; + if (options.fix) { + const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(diagnosticKind, + arkts.getStartPosition(options.node)); + const fixSuggestion = options.fix(options.node); + const suggestionKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_SUGGESTION + ); + const [startPosition, endPosition] = fixSuggestion.range; + const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); + const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create( + suggestionKind, + fixSuggestion.code, + fixSuggestion.title ? fixSuggestion.title : '', + sourceRange + ); + arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo); + } else { + arkts.Diagnostic.logDiagnostic(diagnosticKind, arkts.getStartPosition(options.node)); + } + } + + getMainPages(): string[] { + if (!this.projectConfig) { + return []; + } + const { moduleRootPath, aceModuleJsonPath } = this.projectConfig; + if (!aceModuleJsonPath) { + return []; + } + const moduleConfig = readJSON(aceModuleJsonPath); + if (!moduleConfig) { + return []; + } + if (!moduleConfig.module || !moduleConfig.module.pages) { + return []; + } + const pagesPath = moduleConfig.module.pages; + const matcher = /\$(?[_A-Za-z]+):(?[_A-Za-z]+)/.exec(pagesPath); + if (matcher && matcher.groups) { + const { directory, filename } = matcher.groups; + const mainPagesPath = path.resolve(moduleRootPath, BASE_RESOURCE_PATH, directory, `${filename}.json`); + const mainPages = readJSON(mainPagesPath); + if (!mainPages) { + return []; + } + if (!mainPages.src || !Array.isArray(mainPages.src)) { + return []; + } + return mainPages.src.map((page) => path.resolve(moduleRootPath, ETS_PATH, `${page}.ets`)); + } else { + return []; + } + } + + private format(content: string, placeholders: object): string { + return Object.entries(placeholders).reduce((content, [placehoderName, placehoderValue]) => { + // Fixed a bug where $$ was converted to $ + placehoderValue = placehoderValue.split('$$').join('$$$$'); + return content.replace(`{{${placehoderName}}}`, placehoderValue); + }, content); + } +} + +class ConcreteUISyntaxRuleProcessor implements UISyntaxRuleProcessor { + protected context: UISyntaxRuleContext; + protected handlers: UISyntaxRuleHandler[]; + + constructor(rules: Array) { + this.context = new ConcreteUISyntaxRuleContext(); + this.handlers = rules.reduce((handlers, rule) => { + if (Array.isArray(rule)) { + const [RuleConstructor, level] = rule; + if (level !== 'none') { + handlers.push(new RuleConstructor(this.context, level)); + } + } else { + handlers.push(rule.setup(this.context)); + } + return handlers; + }, []); + } + + beforeTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.beforeTransform(); + } + } + } + + afterTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.afterTransform(); + } + } + } + + parsed(node: arkts.AstNode): void { + for (const handler of this.handlers) { + handler.parsed?.(node); + } + } + + checked(node: arkts.AstNode): void { + for (const handler of this.handlers) { + handler.checked?.(node); + } + } + + setProjectConfig(projectConfig: ProjectConfig): void { + this.context.projectConfig = projectConfig; + } +} + +export function createUISyntaxRuleProcessor(rules: Array): UISyntaxRuleProcessor { + return new ConcreteUISyntaxRuleProcessor(rules); } diff --git a/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts b/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts new file mode 100644 index 0000000000000000000000000000000000000000..47f7b4ae44bc2a1a17c77aa06f2c5b2afa26b072 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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 { getCallee, isBuildInComponent } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + + +class AttributeNoInvokeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + cannotInitializePrivateVariables: `'{{componentNode}}' does not meet UI component syntax.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isExpressionStatement(node) && !arkts.isIdentifier(node.expression)) { + this.attributeNoInvoke(node); + } + } + + private attributeNoInvoke(node: arkts.AstNode): void { + const childNode = node.getChildren(); + if (!Array.isArray(childNode) || childNode.length < 1) { + return; + } + // Determine if the last chained property is an identifier + if (!arkts.isMemberExpression(childNode[0]) || !arkts.isIdentifier(childNode[0].property)) { + return; + } + if (!arkts.isCallExpression(childNode[0].object)) { + return; + } + const callee = getCallee(childNode[0].object); + + // Determine whether it is a built-in component + if (callee && isBuildInComponent(this.context, callee.name)) { + this.report({ + node, + message: this.messages.cannotInitializePrivateVariables, + data: { + componentNode: node.dumpSrc(), + }, + }); + } + } +}; + +export default AttributeNoInvokeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts index c3678f1fe2e25a39bab9c7eb0b115b71196488d8..10cb57d438516f4e72e710fe6d7d43340b689404 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts @@ -14,87 +14,125 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, getAnnotationUsage, PresetDecorators, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const BUILD_NAME: string = 'build'; -const BUILD_ROOT_NUM: number = 1; const STATEMENT_LENGTH: number = 1; +const BUILD_COUNT_LIMIT: number = 1; -function isBuildOneRoot(statements: readonly arkts.Statement[], buildNode: arkts.Identifier, - context: UISyntaxRuleContext): void { - if (statements.length > STATEMENT_LENGTH && buildNode) { - context.report({ - node: buildNode, - message: rule.messages.invalidBuildRootCount, - }); - } -} - -function checkBuildRootNode(node: arkts.AstNode, context: UISyntaxRuleContext): void { - const loadedContainerComponents = context.containerComponents; - if (!arkts.isStructDeclaration(node)) { - return; - } - const entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); - node.definition.body.forEach(member => { - // Determine the number of root node - if (!arkts.isMethodDefinition(member) || getIdentifierName(member.name) !== BUILD_NAME) { - return; +class BuildRootNodeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidEntryBuildRoot: `In an '@Entry' decorated component, the 'build' function can have only one root node, which must be a container component.`, + invalidBuildRoot: `The 'build' function can have only one root node.`, + }; } - const blockStatement = member.scriptFunction.body; - if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { - return; - } - const buildNode = member.scriptFunction.id; - const statements = blockStatement.statements; - // rule1: The 'build' method cannot have more than one root node. - if (buildNode) { - isBuildOneRoot(statements, buildNode, context); + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); + node.definition.body.forEach((member) => { + if (!arkts.isMethodDefinition(member) || getIdentifierName(member.name) !== BUILD_NAME) { + return; + } + const blockStatement = member.scriptFunction.body; + const buildNode = member.scriptFunction.id; + if (!blockStatement || !arkts.isBlockStatement(blockStatement) || !buildNode) { + return; + } + const statements = blockStatement.statements; + let buildCount = 0; + // rule1: The 'build' method cannot have more than one root node. + if (statements.length > STATEMENT_LENGTH) { + if (!this.isBuildOneRoot(statements, buildCount)) { + this.report({ + node: buildNode, + message: entryDecoratorUsage ? this.messages.invalidEntryBuildRoot : this.messages.invalidBuildRoot, + }); + } + } + // rule2: its 'build' function can have only one root node, which must be a container component. + if (!statements.length || !entryDecoratorUsage) { + return; + } + this.validateContainerInBuild(statements, buildNode); + }); } - if (statements.length !== BUILD_ROOT_NUM) { - return; + + private isBuildOneRoot( + statements: readonly arkts.Statement[], + buildCount: number + ): boolean { + statements.forEach(statement => { + if (!arkts.isExpressionStatement(statement)) { + return; + } + if (!statement.expression) { + return; + } + const componentName = this.getComponentName(statement.expression); + if (componentName && componentName !== 'hilog') { + buildCount++; + } + }); + return buildCount <= BUILD_COUNT_LIMIT; } - // Determine whether it is a container component - const expressionStatement = statements[0]; - if (!arkts.isExpressionStatement(expressionStatement)) { - return; + + private validateContainerInBuild(statements: readonly arkts.Statement[], buildNode: arkts.Identifier): void { + const expressionStatement = statements[0]; + if (!arkts.isExpressionStatement(expressionStatement)) { + return; + } + const callExpression = expressionStatement.expression; + if (!arkts.isCallExpression(callExpression)) { + return; + } + let componentName = this.getComponentName(callExpression); + if (!componentName) { + return; + } + let isContainer = this.isContainerComponent(componentName); + if (!isContainer) { + this.report({ + node: buildNode, + message: this.messages.invalidEntryBuildRoot, + }); + } } - const callExpression = expressionStatement.expression; - if (!arkts.isCallExpression(callExpression)) { - return; + + private isContainerComponent(componentName: string): boolean { + const loadedContainerComponents = this.context.componentsInfo?.containerComponents; + if (!componentName || !loadedContainerComponents) { + return false; + } + return loadedContainerComponents.includes(componentName); } - const componentName = callExpression.expression.dumpSrc(); - let isContainer: boolean = false; - loadedContainerComponents?.forEach(container => { - if (componentName.includes(container)) { - isContainer = true; - } - }); - // rule2: If the component is decorated by '@Entry', - // its 'build' function can have only one root node, which must be a container component. - if (entryDecoratorUsage && !isContainer && buildNode) { - context.report({ - node: buildNode, - message: rule.messages.invalidBuildRoot, - }); + + private getComponentName(node: arkts.AstNode): string | undefined { + let children = node.getChildren(); + let componentName: string | undefined; + + while (true) { + if (!children || children.length === 0) { + return undefined; + } + + const firstChild = children[0]; + + if (arkts.isIdentifier(firstChild)) { + componentName = getIdentifierName(firstChild); + return componentName; + } + + if (!arkts.isMemberExpression(firstChild) && !arkts.isCallExpression(firstChild)) { + return undefined; + } + + children = firstChild.getChildren(); + } } - }); } -const rule: UISyntaxRule = { - name: 'build-root-node', - messages: { - invalidBuildRootCount: `The 'build' method cannot have more than one root node.`, - invalidBuildRoot: `If the component is decorated by '@Entry', its 'build' function can have only one root node, which must be a container component.` - }, - setup(context) { - return { - parsed: (node): void => { - checkBuildRootNode(node, context); - }, - }; - }, -}; - -export default rule; +export default BuildRootNodeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b97c5808d27b57214db3474174e2cc301dd112c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts @@ -0,0 +1,138 @@ +/* + * Copyright (c) 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 { getIdentifierName, PresetDecorators, BUILD_NAME, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class BuilderParamDecoratorCheckRule extends AbstractUISyntaxRule { + private structNameWithMultiplyBuilderParam: string[] = []; + private structNameWithoutBuilderParam: string[] = []; + + public setup(): Record { + return { + onlyOneBuilderParamProperty: `In the trailing lambda case, '{{structName}}' must have one and only one property decorated with @BuilderParam, and its @BuilderParam expects no parameter.`, + }; + } + + public beforeTransform(): void { + this.structNameWithMultiplyBuilderParam = []; + } + + public parsed(node: arkts.AstNode): void { + this.getStructNameWithMultiplyBuilderParam(node); + this.checkComponentInitialize(node); + } + + private getStructNameWithMultiplyBuilderParam( + node: arkts.AstNode, + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + let count: number = 0; + let structName: string = member.definition.ident.name ?? ''; + member.definition.body.forEach((item) => { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + const builderParam = findDecorator(item, PresetDecorators.BUILDER_PARAM); + + if (builderParam) { + count++; + } + }); + if (count > 1) { + this.structNameWithMultiplyBuilderParam.push(structName); + } + if (count === 0) { + this.structNameWithoutBuilderParam.push(structName); + } + }); + } + + private isInBuild(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let structNode = node.parent; + while (!arkts.isMethodDefinition(structNode) || getIdentifierName(structNode.name) !== BUILD_NAME) { + if (!structNode.parent) { + return false; + } + structNode = structNode.parent; + } + return arkts.isMethodDefinition(structNode) && getIdentifierName(structNode.name) === BUILD_NAME; + } + + private hasBlockStatement(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let parentNode = node.parent; + const siblings = parentNode.getChildren(); + if (!Array.isArray(siblings) || siblings.length < 2) { + return false; + } + if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { + return true; + } + if (arkts.isBlockStatement(siblings[1])) { + return true; + } + return false; + } + + private checkComponentInitialize( + node: arkts.AstNode, + ): void { + if (!node.parent) { + return; + } + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + let structName: string = getIdentifierName(node); + if (!arkts.isIdentifier(node) || !(this.structNameWithMultiplyBuilderParam.includes(structName) || + this.structNameWithoutBuilderParam.includes(structName))) { + return; + } + if (!this.hasBlockStatement(node)) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + if (!this.isInBuild(node)) { + return; + } + this.report({ + node: node, + message: this.messages.onlyOneBuilderParamProperty, + data: { structName }, + }); + } +} + +export default BuilderParamDecoratorCheckRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts index 7dfa570b04da98eeea09a1f315c47adcc1e2b67c..21cab0ab1a3c1015fd439f407dc13579f51e2121 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts @@ -15,79 +15,78 @@ import * as arkts from '@koalaui/libarkts'; import { getClassPropertyName, isPrivateClassProperty } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function addProperty(item: arkts.AstNode, structName: string, privateMap: Map): void { - if (!arkts.isClassProperty(item) || !isPrivateClassProperty(item)) { - return; - } - // Check if structName already exists in privateMap - if (privateMap.has(structName)) { - // If it exists, retrieve the current string[] and append the new content - privateMap.get(structName)?.push(getClassPropertyName(item)); - } else { - // If it doesn't exist, create a new string[] and add the content - privateMap.set(structName, [getClassPropertyName(item)]); - } -} +class CheckConstructPrivateParameterRule extends AbstractUISyntaxRule { + private privatePropertyMap: Map = new Map(); + + public setup(): Record { + return { + cannotInitializePrivateVariables: `Property '{{propertyName}}' is private and can not be initialized through the component constructor.`, + }; + } + + public beforeTransform(): void { + this.privatePropertyMap = new Map(); + } -function checkPrivateVariables( - node: arkts.AstNode, - context: UISyntaxRuleContext, - privateMap: Map -): void { - // Check if the current node is the root node - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; - } - const structName: string = member.definition.ident?.name ?? ''; - member.definition?.body?.forEach((item) => { - addProperty(item, structName, privateMap); - }); - }); - } - if (!arkts.isCallExpression(node)) { - return; - } - const componentName = node.expression.dumpSrc(); - // If the initialization is for a component with private properties - if (!privateMap.has(componentName)) { - return; - } - node.arguments?.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - const propertyName: string = property.key?.dumpSrc() ?? ''; - if (privateMap.get(componentName)!.includes(propertyName)) { - context.report({ - node: property, - message: rule.messages.cannotInitializePrivateVariables, - data: { - propertyName: propertyName, - }, + public parsed(node: arkts.StructDeclaration): void { + // Check if the current node is the root node + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || !member.definition.ident.name) { + return; + } + const structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.addProperty(item, structName); + }); + }); + } + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // If the initialization is for a component with private properties + if (!this.privatePropertyMap.has(componentName)) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName: string = property.key.name; + if (this.privatePropertyMap.get(componentName)!.includes(propertyName)) { + this.report({ + node: property, + message: this.messages.cannotInitializePrivateVariables, + data: { + propertyName: propertyName, + }, + }); + } + }); }); - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'check-construct-private-parameter', - messages: { - cannotInitializePrivateVariables: `Property '{{propertyName}}' is private and can not be initialized through the component constructor.`, - }, - setup(context) { - let privateMap: Map = new Map(); - return { - parsed: (node): void => { - checkPrivateVariables(node, context, privateMap); - }, - }; - }, -}; + private addProperty(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item) || !isPrivateClassProperty(item)) { + return; + } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } + // Check if structName already exists in privateMap + if (this.privatePropertyMap.has(structName)) { + // If it exists, retrieve the current string[] and append the new content + this.privatePropertyMap.get(structName)!.push(propertyName); + } else { + // If it doesn't exist, create a new string[] and add the content + this.privatePropertyMap.set(structName, [propertyName]); + } + } +} -export default rule; \ No newline at end of file +export default CheckConstructPrivateParameterRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts index 8448fa4351390e77f69b24b28174ae79de7d5008..90c6a82eda041ef9e6a4a36cd1651ab4c8f68e70 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts @@ -14,64 +14,74 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; import { + forbiddenUseStateType, getAnnotationUsage, getClassPropertyAnnotationNames, getClassPropertyName, getClassPropertyType, PresetDecorators } from '../utils'; -function checkDecoratedPropertyType( - member: arkts.AstNode, - context: UISyntaxRuleContext, - relationship: Record -): void { - if (!arkts.isClassProperty(member)) { - return; +const forbiddenUseStateTypeForDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.OBJECT_LINK, + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_LINK, +]; + +class CheckDecoratedPropertyTypeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidDecoratedPropertyType: `The '@{{decoratorName}}' property '{{propertyName}}' cannot be a '{{propertyType}}' object.`, + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + node.definition.body.forEach(member => { + this.checkDecoratedPropertyType(member, forbiddenUseStateTypeForDecorators, forbiddenUseStateType); + }); } - const propertyName = getClassPropertyName(member); - const propertyType = getClassPropertyType(member); - const propertyAnnotationNames = getClassPropertyAnnotationNames(member); - Object.entries(relationship).forEach(([decoratorName, invalidPropertyTypes]) => { - if (propertyAnnotationNames.some(annotationName => annotationName === decoratorName) && - invalidPropertyTypes - .some(invalidPropertyType => invalidPropertyType === propertyType)) { - if (!arkts.isClassProperty || member.key === undefined) { - return; - } - const errorNode = member.key; - context.report({ + + private checkDecoratedPropertyType( + member: arkts.AstNode, + annoList: string[], + typeList: string[] + ): void { + if (!arkts.isClassProperty(member)) { + return; + } + const propertyName = getClassPropertyName(member); + const propertyType = getClassPropertyType(member); + if (!propertyName || !propertyType) { + return; + } + const propertyAnnotationNames: string[] = getClassPropertyAnnotationNames(member); + const decoratorName: string | undefined = + propertyAnnotationNames.find((annotation) => annoList.includes(annotation)); + const isType: boolean = typeList.includes(propertyType); + if (!member.key) { + return; + } + const errorNode = member.key; + if (decoratorName && isType) { + this.report({ node: errorNode, - message: rule.messages.invalidDecoratedPropertyType, + message: this.messages.invalidDecoratedPropertyType, data: { decoratorName, propertyName, propertyType }, }); } - }); + } } -const rule: UISyntaxRule = { - name: 'check-decorated-property-type', - messages: { - invalidDecoratedPropertyType: `The {{decoratorName}} property '{{propertyName}}' cannot be a '{{propertyType}}' object.`, - }, - setup(context) { - const relationship: Record = { - [PresetDecorators.STATE]: ['CustomDialogController'], - }; - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - if (!componentDecorator) { - return; - } - node.definition.body.forEach(member => { - checkDecoratedPropertyType(member, context, relationship); - }); - }, - }; - }, -}; - -export default rule; \ No newline at end of file +export default CheckDecoratedPropertyTypeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts b/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts new file mode 100644 index 0000000000000000000000000000000000000000..8081137833a846d3299563d85c484241fceca8ce --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/check-property-modifiers.ts @@ -0,0 +1,134 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { + getClassPropertyAnnotationNames, + getClassPropertyName, + isPrivateClassProperty, + isProtectedClassProperty, + isPublicClassProperty, + PresetDecorators +} from '../utils'; + + +class CheckPropertyModifiersRule extends AbstractUISyntaxRule { + private static readonly noPublicDecorators: string[] = [ + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_LINK, + ]; + + private static readonly noPrivateDecorators: string[] = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + ]; + + public setup(): Record { + return { + invalidPublic: `The {{decoratorName}} decorated '{{propertyName}}' cannot be declared as public.`, + invalidPrivate: `The {{decoratorName}} decorated '{{propertyName}}' cannot be declared as private.`, + invalidProtected: `The member attributes of a struct can not be protected.` + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + if (arkts.isDefaultAccessModifierClassProperty(member)) { + return; + } + const propertyName = getClassPropertyName(member); + if (!propertyName) { + return; + } + const propertyAnnotationNames = getClassPropertyAnnotationNames(member); + propertyAnnotationNames.forEach((propertyAnnotationName) => { + this.checkInvalidPublic(propertyAnnotationName, propertyName, member); + this.checkInvalidPrivate(propertyAnnotationName, propertyName, member); + }); + this.checkInvalidProtected(propertyName, member); + }); + } + + private checkInvalidPublic(propertyAnnotationName: string, propertyName: string, + member: arkts.ClassProperty + ): void { + if (!CheckPropertyModifiersRule.noPublicDecorators.includes(propertyAnnotationName) || + !isPublicClassProperty(member)) { + return; + } + const errorNode = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + CheckPropertyModifiersRule.noPublicDecorators.includes(annotation.expr.name) + ); + if (!errorNode) { + return; + } + this.report({ + node: errorNode, + message: this.messages.invalidPublic, + data: { + decoratorName: propertyAnnotationName, + propertyName: propertyName, + } + }); + } + + private checkInvalidPrivate(propertyAnnotationName: string, propertyName: string, + member: arkts.ClassProperty + ): void { + if (!CheckPropertyModifiersRule.noPrivateDecorators.includes(propertyAnnotationName) || + !isPrivateClassProperty(member)) { + return; + } + const errorNode = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + CheckPropertyModifiersRule.noPrivateDecorators.includes(annotation.expr.name) + ); + if (!errorNode) { + return; + } + this.report({ + node: errorNode, + message: this.messages.invalidPrivate, + data: { + decoratorName: propertyAnnotationName, + propertyName: propertyName, + }, + }); + } + + private checkInvalidProtected(propertyName: string, member: arkts.ClassProperty): void { + if (!isProtectedClassProperty(member)) { + return; + } + this.report({ + node: member, + message: this.messages.invalidProtected, + data: { + propertyName: propertyName, + } + }); + } +} + +export default CheckPropertyModifiersRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..7230b8f6d1878913a70a81b8b744fbda50252cfa --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 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 { getAnnotationUsage, getIdentifierName, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ComponentComponentV2InitCheckRule extends AbstractUISyntaxRule { + private componentV1WithLinkList: string[] = []; + + public setup(): Record { + return { + componentInitLinkCheck: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`, + }; + } + + public beforeTransform(): void { + this.componentV1WithLinkList = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initComponentV1WithLinkList(node); + this.checkComponentInitLink(node); + } + + private initComponentV1WithLinkList(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !hasAnnotation(member?.definition.annotations, PresetDecorators.COMPONENT_V1)) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + member.definition?.body?.forEach((item) => { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + if (item.annotations.some(annotation => annotation.expr && + getIdentifierName(annotation.expr) === PresetDecorators.LINK)) { + this.componentV1WithLinkList.push(structName); + } + }); + }); + } + + private checkComponentInitLink(node: arkts.AstNode): void { + if (!arkts.isIdentifier(node) || !this.componentV1WithLinkList.includes(getIdentifierName(node))) { + return; + } + if (!node.parent) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + if (getAnnotationUsage(structNode, PresetDecorators.COMPONENT_V2) !== undefined) { + const parentNode = node.parent; + this.report({ + node: parentNode, + message: this.messages.componentInitLinkCheck, + fix: () => { + return { + title: 'Remove the component', + range: [parentNode.startPosition, parentNode.endPosition], + code: '', + }; + } + }); + } + } +} + +export default ComponentComponentV2InitCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts index ce7afdf8ffdd1b6362c5692a8d3f87d343afd724..70714c3b382345f43e170d7754cb75833dae0bbf 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts @@ -14,164 +14,176 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Report an Observed version violation error -function reportObservedConflict( - node: arkts.ClassProperty, - context: UISyntaxRuleContext, - message: string -): void { - node.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc()) { - context.report({ - node: anno, - message: message, - data: { - annotation: anno.expr?.dumpSrc(), +import { PresetDecorators, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const v1ComponentDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, +]; + +class ComponentComponentV2MixUseCheckRule extends AbstractUISyntaxRule { + private observedV2Names: Set = new Set(); + + public setup(): Record { + return { + observedv2_v1: `The type of the @{{annotation}} property cannot be a class decorated with '@ObservedV2'.` + }; + } + + public beforeTransform(): void { + this.observedV2Names = new Set(); + } + + public parsed(node: arkts.AstNode): void { + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + this.findAllObserved(node); + this.findAllTSTypeAliasDeclaration(node); + } + + if (arkts.isStructDeclaration(node)) { + this.processComponentAnnotations(node); + } + } + + private findAllObserved(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + + const annotationName = getIdentifierName(anno.expr); + + if (annotationName === PresetDecorators.OBSERVED_V2) { + const componentV2Name = node.definition?.ident?.name; + componentV2Name ? this.observedV2Names.add(componentV2Name) : null; } }); } - }); -} -function processNode( - node: arkts.ClassProperty, - annotationName: string, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - if (observedV1Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V2) { - reportObservedConflict(node, context, rule.messages.observedv1_v2); - break; - } - if (observedV2Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V1) { - reportObservedConflict(node, context, rule.messages.observedv2_v1); - break; + for (const child of node.getChildren()) { + this.findAllObserved(child); + } + } + + private findAllTSTypeAliasDeclaration(node: arkts.AstNode): void { + if ( + arkts.nodeType(node) === + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION + ) { + for (const child of node.getChildren()) { + if (arkts.isIdentifier(child)) { + const typeName = getIdentifierName(child); + this.findAllObservedType(node, typeName); + } } } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); + + for (const child of node.getChildren()) { + this.findAllTSTypeAliasDeclaration(child); } } -} -function traverseTree( - node: arkts.AstNode, - annotationName: string, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - if (arkts.isClassProperty(node)) { - processNode(node, annotationName, observedV1Name, observedV2Name, context); - } - const children = node.getChildren(); - for (const child of children) { - traverseTree(child, annotationName, observedV1Name, observedV2Name, context); + private findAllObservedType( + node: arkts.AstNode, + typeName: string + ): void { + if (arkts.isIdentifier(node)) { + const name = getIdentifierName(node); + + if (this.observedV2Names.has(name)) { + this.observedV2Names.add(typeName); + } + } + + for (const child of node.getChildren()) { + this.findAllObservedType(child, typeName); + } } -} -function findAllObserved(node: arkts.AstNode, observedV1Name: Set, observedV2Name: Set): void { - if (arkts.isClassDeclaration(node)) { - node.definition?.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V1) { - const componentV1Name = node?.definition?.ident?.name; - componentV1Name ? observedV1Name.add(componentV1Name) : null; + private processComponentAnnotations( + node: arkts.StructDeclaration + ): void { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; } - if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V2) { - const componentV2Name = node?.definition?.ident?.name; - componentV2Name ? observedV2Name.add(componentV2Name) : null; + const annotationName = getIdentifierName(anno.expr); + if ( + annotationName === PresetDecorators.COMPONENT_V2 || + annotationName === PresetDecorators.COMPONENT_V1 + ) { + this.traverseTree(node, annotationName); } }); } - const children = node.getChildren(); - for (const child of children) { - findAllObserved(child, observedV1Name, observedV2Name); + + private traverseTree( + node: arkts.AstNode, + annotationName: string + ): void { + if (arkts.isClassProperty(node)) { + this.processNode(node, annotationName); + } + + for (const child of node.getChildren()) { + this.traverseTree(child, annotationName); + } } -} -function findAllTSTypeAliasDeclaration( - node: arkts.AstNode, - observedV1Name: Set, - observedV2Name: Set -): void { - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION) { - node.getChildren().forEach((child) => { - if (arkts.isIdentifier(child)) { - const typeName = child.dumpSrc(); - findAllObservedType(node, typeName, observedV1Name, observedV2Name); + private processNode( + node: arkts.ClassProperty, + annotationName: string + ): void { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + const name = getIdentifierName(currentNode); + + if ( + annotationName === PresetDecorators.COMPONENT_V1 && + this.observedV2Names.has(name) + ) { + this.checkObservedConflict(node, v1ComponentDecorators); + break; + } } - }); - } - const children = node.getChildren(); - for (const child of children) { - findAllTSTypeAliasDeclaration(child, observedV1Name, observedV2Name); + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } } -} -function findAllObservedType( - node: arkts.AstNode, - typeName: string, - observedV1Name: Set, - observedV2Name: Set -): void { - if (arkts.isIdentifier(node) && observedV1Name.has(node.dumpSrc())) { - observedV1Name.add(typeName); - } - if (arkts.isIdentifier(node) && observedV2Name.has(node.dumpSrc())) { - observedV2Name.add(typeName); - } - const children = node.getChildren(); - for (const child of children) { - findAllObservedType(child, typeName, observedV1Name, observedV2Name); - } -} + private checkObservedConflict( + node: arkts.ClassProperty, + componentDecorators: string[] + ): void { + node.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } -function processComponentAnnotations( - node: arkts.StructDeclaration, - observedV1Name: Set, - observedV2Name: Set, - context: UISyntaxRuleContext -): void { - node?.definition?.annotations.forEach((anno) => { - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2) { - traverseTree(node, PresetDecorators.COMPONENT_V2, observedV1Name, observedV2Name, context); - } - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V1) { - traverseTree(node, PresetDecorators.COMPONENT_V1, observedV1Name, observedV2Name, context); - } - }); + const annotationName = getIdentifierName(anno.expr); + if (annotationName && componentDecorators.includes(annotationName)) { + this.report({ + node: anno, + message: this.messages.observedv2_v1, + data: { + annotation: annotationName, + }, + }); + } + }); + } } -const rule: UISyntaxRule = { - name: 'component-componentV2-mix-use-check', - messages: { - observedv1_v2: `The type of the @{{annotation}} Decorator property can not be a class decorated with @Observed.`, - observedv2_v1: `The type of the @{{annotation}} Decorator property can not be a class decorated with @ObservedV2.` - }, - setup(context) { - let observedV1Name: Set = new Set(); - let observedV2Name: Set = new Set(); - return { - parsed: (node): void => { - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - findAllObserved(node, observedV1Name, observedV2Name); - findAllTSTypeAliasDeclaration(node, observedV1Name, observedV2Name); - } - if (arkts.isStructDeclaration(node)) { - processComponentAnnotations(node, observedV1Name, observedV2Name, context); - } - }, - }; - }, -}; - -export default rule; \ No newline at end of file +export default ComponentComponentV2MixUseCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts b/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts index 082d768d1abf99e1fffe480a88fdc4e829fa0afb..7db39458228955d4c9ae1a445ab357ba01ce48a7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.ts @@ -15,43 +15,47 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'componentV2-mix-check', - messages: { - conflictWithComponentV2: `The struct '{{structName}}' can not be decorated with '@ComponentV2' and '@Component', '@Reusable', '@CustomDialog' at the same time.`, - }, - setup(context) { +class ComponentV2MixCheckRule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const structName = node.definition.ident?.name ?? ''; - const structNameNode = node.definition.ident; - if (!structNameNode) { - return; - } - // Check if the struct has the '@ComponentV2' annotation - const hasComponentV2 = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - if (!hasComponentV2) { - return; - } - // Check for the presence of conflicting decorators: '@Component', '@Reusable', '@CustomDialog' - const hasComponent = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - const hasReusable = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); - const hasCustomDialog = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); - if (hasComponent || hasReusable || hasCustomDialog) { - context.report({ - node: structNameNode, - message: rule.messages.conflictWithComponentV2, - data: { structName }, - }); - } - }, + conflictWithComponentV2: `The struct '{{structName}}' can not be decorated with '@ComponentV2' and '@Component', '@Reusable', '@CustomDialog' at the same time.`, }; - }, -}; + } -export default rule; + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const definition = node.definition; + if (!definition) { + return; + } + const structNameNode = definition.ident; + if (!structNameNode) { + return; + } + const structName = structNameNode.name ?? ''; + // Check if the struct has the '@ComponentV2' annotation + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + if (!componentV2Decorator) { + return; + } + + // Check for conflicting decorators + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + const reusableDecorator = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); + const customDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); + + if (componentDecorator || reusableDecorator || customDialogDecorator) { + this.report({ + node: structNameNode, + message: this.messages.conflictWithComponentV2, + data: { structName }, + }); + } + } +} + +export default ComponentV2MixCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts index b60587aeece7799fc34f3f48b37c5dc0aad66a0c..47d5088ec97e6e2df8055d203173b8a9bbe8b613 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts @@ -14,155 +14,346 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Helper functions for rules -const hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V2); - -const hasComponent = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V1); -function checkMultipleBuiltInDecorators(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; - const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); - if (appliedBuiltInDecorators.length > 1) { - member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - reportMultipleBuiltInDecoratorsError(context, annotation, annotationsName, builtInDecorators); - }); - } -}; +import { + getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage, getClassPropertyName, getIdentifierName +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; -function reportMultipleBuiltInDecoratorsError(context: UISyntaxRuleContext, annotation: arkts.AstNode, - annotationsName: string | undefined, builtInDecorators: string[]): void { - if (annotationsName && builtInDecorators.includes(annotationsName)) { - context.report({ - node: annotation, - message: rule.messages.multipleBuiltInDecorators, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); +class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { + private componentV2PropertyMap: Map> = new Map(); + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in annotations.`, + paramRequiresRequire: `When a variable decorated with '@Param' is not assigned a default value, it must also be decorated with '@Require'.`, + requireOnlyWithParam: `In a struct decorated with '@ComponentV2', '@Require' can only be used with '@Param' or '@BuilderParam'.`, + localNeedNoInit: `The '{{decoratorName}}' property '{{key}}' in the custom component '{{componentName}}' cannot be initialized here (forbidden to specify).`, + useStateDecoratorsWithProperty: `'@{{annotationName}}' can only decorate member property.`, }; - }, - }); - } -} - -function checkDecoratorOnlyInisComponentV2(context: UISyntaxRuleContext, member: arkts.ClassProperty, - node: arkts.StructDeclaration, hasisComponentV2: boolean, hasComponent: boolean): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; - member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - if (annotationsName && builtInDecorators.includes(annotationsName) && !hasisComponentV2 && !hasComponent) { - reportDecoratorOnlyInisComponentV2Error(context, annotation, annotationsName, node); } - }); -}; -function reportDecoratorOnlyInisComponentV2Error(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, - annotationsName: string, node: arkts.StructDeclaration): void { - context.report({ - node: annotation, - message: rule.messages.decoratorOnlyInisComponentV2, - data: { annotationsName }, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(node); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.COMPONENT_V2}\n`, - }; - }, - }); -} - -function checkParamRequiresRequire(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && - !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { - const memberKey = member.key; - context.report({ - node: memberKey, - message: rule.messages.paramRequiresRequire, - fix: (memberKey) => { - const startPosition = arkts.getStartPosition(memberKey); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.REQUIRE} `, - }; - }, - }); - } -}; + public beforeTransform(): void { + this.componentV2PropertyMap = new Map(); + } -function checkRequireOnlyWithParam(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { - const requireDecorator = member.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.REQUIRE - ); - if (requireDecorator && !propertyDecorators.includes(PresetDecorators.PARAM)) { - context.report({ - node: requireDecorator, - message: rule.messages.requireOnlyWithParam, - fix: (requireDecorator) => { - const startPosition = arkts.getStartPosition(requireDecorator); - const endPosition = arkts.getEndPosition(requireDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - } -}; + public parsed(node: arkts.AstNode): void { + this.initComponentV2PropertyMap(node, this.componentV2PropertyMap); + this.checkInitializeRule(node, this.componentV2PropertyMap); + if (arkts.isClassDeclaration(node) && node.definition) { + this.checkuseStateDecoratorsWithProperty(node.definition); + } + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isScriptFunction(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + // Rule 5: Local, Param, Event decorators must be used with Property + this.checkuseStateDecoratorsWithProperty(node); + } + if (!arkts.isStructDeclaration(node)) { + return; + } + this.validateClassPropertyDecorators(node); + } -function validateClassPropertyDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - const isComponentV2 = hasisComponentV2(node); - const isComponent = hasComponent(node); - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + private hasComponentV2Annotation = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, + PresetDecorators.COMPONENT_V2); + + private checkMultipleBuiltInDecorators(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); + if (appliedBuiltInDecorators.length > 1) { + member.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const annotationsName = annotation.expr.name; + this.reportMultipleBuiltInDecoratorsError(annotation, annotationsName, builtInDecorators); + } + }); + } + }; + + private reportMultipleBuiltInDecoratorsError(annotation: arkts.AstNode, + annotationsName: string | undefined, builtInDecorators: string[]): void { + if (annotationsName && builtInDecorators.includes(annotationsName)) { + this.report({ + node: annotation, + message: this.messages.multipleBuiltInDecorators, + }); + } } - const propertyDecorators = getClassPropertyAnnotationNames(member); - // Rule 1: Multiple built-in decorators - checkMultipleBuiltInDecorators(context, member, propertyDecorators); + private checkParamRequiresRequire(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && + !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { + const memberKey = member.key; + this.report({ + node: memberKey, + message: this.messages.paramRequiresRequire, + fix: (memberKey) => { + const startPosition = memberKey.startPosition; + return { + title: 'Add @Require annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.REQUIRE} `, + }; + }, + }); + } + }; - // Rule 2: Built-in decorators only allowed in @isComponentV2 - checkDecoratorOnlyInisComponentV2(context, member, node, isComponentV2, isComponent); + private checkRequireOnlyWithParam(member: arkts.ClassProperty, + propertyDecorators: string[], isComponentV2: boolean): void { + const requireDecorator = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === PresetDecorators.REQUIRE + ); + if (isComponentV2 && + requireDecorator && + !propertyDecorators.includes(PresetDecorators.PARAM) && + !propertyDecorators.includes(PresetDecorators.BUILDER_PARAM)) { + this.report({ + node: requireDecorator, + message: this.messages.requireOnlyWithParam, + fix: (requireDecorator) => { + let startPosition = requireDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = requireDecorator.endPosition; + return { + title: 'Remove the @Require annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + }; - // Rule 3: @Param without default value must be combined with @Require - checkParamRequiresRequire(context, member, propertyDecorators); + private checkuseStateDecoratorsWithProperty( + node: arkts.FunctionDeclaration | + arkts.VariableDeclaration | + arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | + arkts.ClassDefinition | + arkts.TSTypeAliasDeclaration): void { + node.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + builtInDecorators.includes(annotation.expr.name)) { + const annotationName = annotation.expr.name; + this.reportInvalidDecoratorOnMethod(annotation, annotationName); + } + }); + } - // Rule 4: @Require must be used together with @Param - checkRequireOnlyWithParam(context, member, propertyDecorators); - }); -} + private reportInvalidDecoratorOnMethod(annotation: arkts.AnnotationUsage, + annotationName: string): void { + this.report({ + node: annotation, + message: this.messages.useStateDecoratorsWithProperty, + data: { annotationName }, + fix: (annotation) => { + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -const rule: UISyntaxRule = { - name: 'iscomponentV2-state-usage-validation', - messages: { - multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in decorators.`, - decoratorOnlyInisComponentV2: `The '@{{annotationsName}}' decorator can only be used in a 'struct' decorated with '@isComponentV2'.`, - paramRequiresRequire: `When a variable decorated with @Param is not assigned a default value, it must also be decorated with @Require.`, - requireOnlyWithParam: `In a struct decorated with @isComponentV2, @Require can only be used with @Param. ` - }, + private validateClassPropertyDecorators(node: arkts.StructDeclaration): void { + this.checkuseStateDecoratorsWithProperty(node.definition); + const isComponentV2 = this.hasComponentV2Annotation(node); + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const propertyDecorators = getClassPropertyAnnotationNames(member); + // Rule 1: Multiple built-in decorators + this.checkMultipleBuiltInDecorators(member, propertyDecorators); - setup(context) { - return { - parsed: (node): void => { + // Rule 2: @Param without default value must be combined with @Require + this.checkParamRequiresRequire(member, propertyDecorators); - if (!arkts.isStructDeclaration(node)) { - return; + // Rule 3: @Require must be used together with @Param + this.checkRequireOnlyWithParam(member, propertyDecorators, isComponentV2); + }); + } + + private checkDecorator(annoArray: readonly arkts.AnnotationUsage[], decorator: string): boolean { + let flag = false; + annoArray.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + if (annoName === decorator) { + flag = true; + } + }); + return flag; + } + + // Define a function to add property data to the property map + private addPropertyMap( + structName: string, + propertyName: string, + annotationName: string, + propertyMap: Map> + ): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); } - validateClassPropertyDecorators(context, node); - }, - }; - }, + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } + } + + // Iterate through the incoming componentv2 node to see if there are any state variables for decorator decorations + private initComponentV2PropertyMap( + node: arkts.AstNode, + componentV2PropertyMap: Map> + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !this.checkDecorator(member.definition.annotations, PresetDecorators.COMPONENT_V2)) { + return; + } + let structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.processClassPropertyAnnotations(item, structName, componentV2PropertyMap); + }); + }); + } + + private processClassPropertyAnnotations( + item: arkts.AstNode, + structName: string, + componentV2PropertyMap: Map> + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + let propertyName: string = getIdentifierName(item.key); + // If there is no decorator, it is a regular type + if (item.annotations.length === 0) { + let annotationName: string = PresetDecorators.REGULAR; + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (annotationName === PresetDecorators.LOCAL) { + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + }); + } + + private checkInitializeRule( + node: arkts.AstNode, + + componentV2PropertyMap: Map>, + ): void { + if (!arkts.isIdentifier(node) || !componentV2PropertyMap.has(getIdentifierName(node)) || !node.parent) { + return; + } + let structName: string = getIdentifierName(node); + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + let parentPropertyMap: Map = new Map(); + structNode.definition.body.forEach((property) => { + if (!arkts.isClassProperty(property)) { + return; + } + let propertyArray: string[] = []; + property.annotations.forEach((annotation) => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return; + } + propertyArray.push(annotation.expr.name); + }); + const classPropertyName = getClassPropertyName(property); + if (!classPropertyName) { + return; + } + parentPropertyMap.set(classPropertyName, propertyArray); + }); + // Gets all the properties recorded by the struct + const childPropertyName: Map = componentV2PropertyMap.get(structName)!; + parentNode.arguments.forEach((argument) => { + if (!arkts.isObjectExpression(argument)) { + return; + } + argument.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key) { + return; + } + const childkeyName = getIdentifierName(property.key); + if (!childPropertyName.has(childkeyName)) { + return; + } + this.reportLocalNeedInit(childPropertyName, childkeyName, property, structName); + }); + }); + } + + private reportLocalNeedInit(childPropertyName: Map, childkeyName: string, + property: arkts.Property, structName: string): void { + if (childPropertyName.get(childkeyName) === PresetDecorators.LOCAL) { + this.report({ + node: property, + message: this.messages.localNeedNoInit, + data: { + decoratorName: '@' + childPropertyName.get(childkeyName)!, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + title: 'Remove the property', + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + if (childPropertyName.get(childkeyName) === PresetDecorators.REGULAR) { + this.report({ + node: property, + message: this.messages.localNeedNoInit, + data: { + decoratorName: PresetDecorators.REGULAR, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + title: 'Remove the property', + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + } }; -export default rule; \ No newline at end of file +export default ComponentV2StateUsageValidationRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd271307d169280f104a1155e215ebe1c8854a76 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -0,0 +1,312 @@ +/* + * Copyright (c) 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 { getClassAnnotationUsage, getIdentifierName, PresetDecorators, getAnnotationUsage, BUILD_NAME, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { + private computedGetters: Map = new Map(); + private computedSetters: Map = new Map(); + + public setup(): Record { + return { + onlyOnGetter: `@Computed can only decorate 'GetAccessor'.`, + onlyInObservedV2: `The '@Computed' can decorate only member method within a 'class' decorated with ObservedV2.`, + componentV2InStruct: `The '@Computed' annotation can only be used in a 'struct' decorated with ComponentV2.`, + noTwoWayBinding: `A property decorated by '@Computed' cannot be used with two-way bind syntax.`, + computedMethodDefineSet: `A property decorated by '@Computed' cannot define a set method.` + }; + } + + public beforeTransform(): void { + this.computedGetters = new Map(); + this.computedSetters = new Map(); + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + this.validateComponentV2InStruct(node); + this.validateStructBody(node); + } + + if (arkts.isClassDeclaration(node)) { + this.validateClassBody(node); + } + } + + private validateStructBody(node: arkts.StructDeclaration): void { + let computedDecorator: arkts.AnnotationUsage | undefined; + node.definition.body.forEach((member) => { + if (arkts.isClassProperty(member)) { + this.validateComputedOnClassProperty(member); + return; + } + + if (arkts.isMethodDefinition(member)) { + const methodName = getIdentifierName(member.name); + computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + if (member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + this.computedSetters.set(methodName, member); + } + + if (methodName === BUILD_NAME) { + this.validateBuildMethod(member); + } + } + }); + + this.validateGetterSetterConflict(); + } + + private validateComputedOnClassProperty(member: arkts.ClassProperty): void { + const computedDecorator = findDecorator(member, PresetDecorators.COMPUTED); + if (computedDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + + private validateComputedMethodKind( + member: arkts.MethodDefinition, + computedDecorator: arkts.AnnotationUsage | undefined, + methodName: string + ): void { + if (computedDecorator) { + const isGetter = member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (!isGetter) { + this.reportValidateComputedMethodKind(computedDecorator); + } else { + this.computedGetters.set(methodName, member); + } + } + } + + private reportValidateComputedMethodKind(computedDecorator: arkts.AnnotationUsage | undefined): void { + if (!computedDecorator) { + return; + } + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateBuildMethod(member: arkts.MethodDefinition): void { + member.scriptFunction.body?.getChildren().forEach((childNode) => { + if (!arkts.isExpressionStatement(childNode)) { + return; + } + const queue: Array = [childNode]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isCallExpression(currentNode)) { + this.validateCallExpression(currentNode); + } + // Continue traversing the child nodes + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + }); + } + + private validateCallExpression(currentNode: arkts.CallExpression): void { + if (!arkts.isIdentifier(currentNode.expression) || getIdentifierName(currentNode.expression) !== '$$') { + return; + } + + currentNode.arguments.forEach((argument) => { + if (arkts.isMemberExpression(argument)) { + const getterName = getIdentifierName(argument.property); + this.reportValidateCallExpression(currentNode, getterName); + } + }); + } + + private reportValidateCallExpression( + currentNode: arkts.CallExpression, + getterName: string + ): void { + if (this.computedGetters.has(getterName)) { + this.report({ + node: currentNode, + message: this.messages.noTwoWayBinding, + }); + } + } + + private validateGetterSetterConflict(): void { + for (const [name] of this.computedGetters) { + if (this.computedSetters.has(name)) { + this.reportValidateGetterSetterConflict(name); + } + } + } + + private reportValidateGetterSetterConflict(name: string): void { + const setter = this.computedSetters.get(name)!; + this.report({ + node: setter, + message: this.messages.computedMethodDefineSet, + fix: (node) => { + const startPosition = node.startPosition; + const endPosition = node.endPosition; + return { + title: 'Remove the set method', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateClassBody(node: arkts.ClassDeclaration): void { + const observedV2Decorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V2); + const observedDecorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V1); + + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + + this.validateComputedInClass(node, member, observedV2Decorator, observedDecorator); + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (!arkts.isIdentifier(member.name)) { + return; + } + const methodName = getIdentifierName(member.name); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + } + }); + } + + private validateComponentV2InStruct(node: arkts.StructDeclaration): void { + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + this.checkComponentV2InStruct(node, member, componentV2Decorator, componentDecorator); + } + }); + } + + private checkComponentV2InStruct( + node: arkts.StructDeclaration | arkts.ClassDeclaration, + member: arkts.MethodDefinition, + componentV2Decorator: arkts.AnnotationUsage | undefined, + componentDecorator: arkts.AnnotationUsage | undefined + ): void { + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (computedDecorator && !componentV2Decorator && !componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = node.startPosition; + const endPosition = node.startPosition; + return { + title: 'Add @ComponentV2 annotation', + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); + } + + if (computedDecorator && !componentV2Decorator && componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = componentDecorator.startPosition; + const endPosition = componentDecorator.endPosition; + return { + title: 'Change @Component to @ComponentV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.COMPONENT_V2}`, + }; + }, + }); + } + } + + private validateComputedInClass( + node: arkts.AstNode, + member: arkts.MethodDefinition, + observedV2Decorator: arkts.AnnotationUsage | undefined, + observedDecorator: arkts.AnnotationUsage | undefined + ): void { + const computedDecorator = findDecorator(member.scriptFunction, PresetDecorators.COMPUTED); + if (computedDecorator && !observedV2Decorator && !observedDecorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = node.startPosition; + return { + title: 'Add @ObservedV2 annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } + if (computedDecorator && !observedV2Decorator && observedDecorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = observedDecorator.startPosition; + const endPosition = observedDecorator.endPosition; + return { + title: 'Change @Observed to @ObservedV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.OBSERVED_V2}`, + }; + }, + }); + } + } +} + +export default ComputedDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts index f893b01ded67683f6d0a0e31f23845cc6a8ac9aa..784cecad5d7f20b648e56ea33384bff7d6718ee6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts @@ -14,99 +14,126 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getClassPropertyAnnotationNames, getClassPropertyName, getIdentifierName, PresetDecorators } from '../utils'; -function recordStructWithLinkDecorators(item: arkts.AstNode, structName: string, linkMap: Map): void { - if (!arkts.isClassProperty(item)) { - return; - } - item.annotations?.forEach((annotation) => { - const annotationName: string = annotation.expr?.dumpSrc() ?? ''; - if (annotationName === '') { - return; + +class ConstructParameterLiteralRule extends AbstractUISyntaxRule { + // Record all @Link and @ObjectLink attributes + private linkMap: Map> = new Map(); + + public setup(): Record { + return { + initializerIsLiteral: `The 'regular' property '{{initializerName}}' cannot be assigned to the '{{parameter}}' property '{{parameterName}}'.`, + }; } - // If the node has properties decorated with Link or ObjectLink, record this structure node - if (annotationName === PresetDecorators.LINK || annotationName === PresetDecorators.OBJECT_LINK) { - linkMap.set(structName, annotationName); + + public beforeTransform(): void { + this.linkMap = new Map(); } - }); -} -function initMap(node: arkts.AstNode, linkMap: Map): void { - // Check if the current node is the root node - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.initMap(node); + this.checkInitializeWithLiteral(node); } - const structName: string = member.definition.ident?.name ?? ''; - if (structName === '') { - return; + + private addLinkProperty( + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!this.linkMap.has(structName)) { + this.linkMap.set(structName, new Map()); + } + const structProperties = this.linkMap.get(structName); + if (!structProperties) { + return; + } + structProperties.set(propertyName, annotationName); } - member.definition?.body?.forEach((item) => { - recordStructWithLinkDecorators(item, structName, linkMap); - }); - }); -} -function checkInitializeWithLiteral(node: arkts.AstNode, context: UISyntaxRuleContext, - linkMap: Map -): void { - if (!arkts.isCallExpression(node)) { - return; - } - const componentName = node.expression.dumpSrc(); - // Only assignments to properties decorated with Link or ObjectLink trigger rule checks - if (!linkMap.has(componentName)) { - return; - } - node.arguments.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - if (property.value === undefined) { - return; - } - const propertyType: arkts.Es2pandaAstNodeType = arkts.nodeType(property.value); - const key: string = property.key?.dumpSrc() ?? ''; - if (key === '') { - return; - } - const value = property.value?.dumpSrc() ? property.value.dumpSrc() : ''; - // If the assignment statement is not of type MemberExpression, throw an error - if (propertyType !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION) { - context.report({ - node: property, - message: rule.messages.cannotInitializeWithLiteral, - data: { - value: value, - annotationName: linkMap.get(componentName)!, - key: key, - }, + private recordStructWithLinkDecorators(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item)) { + return; + } + const ClassPropertyAnnotations = getClassPropertyAnnotationNames(item); + + const classPropertyName = getClassPropertyName(item); + if (!classPropertyName) { + return; + } + if (ClassPropertyAnnotations.includes(PresetDecorators.OBJECT_LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.OBJECT_LINK); + } + if (ClassPropertyAnnotations.includes(PresetDecorators.LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.LINK); + } + } + + private initMap(node: arkts.AstNode): void { + // Check if the current node is the root node + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!(arkts.isStructDeclaration(member))) { + return; + } + if (!member.definition || !member.definition.ident || !arkts.isIdentifier(member.definition.ident)) { + return; + } + const structName: string = member.definition.ident.name; + if (structName === '') { + return; + } + member.definition?.body?.forEach((item) => { + this.recordStructWithLinkDecorators(item, structName); + }); }); - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'construct-parameter-literal', - messages: { - cannotInitializeWithLiteral: `Assigning the attribute'{{value}}' to the '@{{annotationName}}' decorated attribute '{{key}}' is not allowed.`, - }, - setup(context) { - let linkMap: Map = new Map(); - return { - parsed: (node): void => { - initMap(node, linkMap); - checkInitializeWithLiteral(node, context, linkMap); - }, - }; - }, -}; + private checkInitializeWithLiteral(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // Only assignments to properties decorated with Link or ObjectLink trigger rule checks + if (!this.linkMap.has(componentName)) { + return; + } + const structLinkMap = this.linkMap.get(componentName); + if (!structLinkMap) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !property.value) { + return; + } + const propertyName: string = getIdentifierName(property.key); + if (propertyName === '' || !structLinkMap.has(propertyName)) { + return; + } + // If the statement type is single-level MemberExpression or Identifier, construct-parameter is validated. + if (arkts.isMemberExpression(property.value) && arkts.isThisExpression(property.value.object)) { + return; + } + if (arkts.isIdentifier(property.value)) { + return; + } + const initializerName = property.value.dumpSrc().replace(/\(this\)/g, 'this'); + this.report({ + node: property, + message: this.messages.initializerIsLiteral, + data: { + initializerName: initializerName, + parameter: `@${structLinkMap.get(propertyName)}`, + parameterName: propertyName, + }, + }); + }); + }); + } +} -export default rule; \ No newline at end of file +export default ConstructParameterLiteralRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a4d5695c7fa398390b6991e3c00273a671646b3 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts @@ -0,0 +1,316 @@ +/* + * Copyright (c) 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 { + getIdentifierName, + getClassPropertyName, + getClassPropertyAnnotationNames, + PresetDecorators, + getAnnotationName, +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +// When a specific decorator is used as a parameter, the assigned decorator is not allowed +const disallowAssignedDecorators: string[] = [ + PresetDecorators.REGULAR, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK, + PresetDecorators.BUILDER_PARAM, PresetDecorators.BUILDER, PresetDecorators.STATE, + PresetDecorators.PROP_REF, PresetDecorators.PROVIDE, PresetDecorators.CONSUME, + PresetDecorators.BUILDER, +]; +// The decorator structure prohibits initializing the assignment list +const restrictedDecoratorInitializations: Map = new Map([ + [PresetDecorators.REGULAR, [PresetDecorators.OBJECT_LINK, PresetDecorators.LINK]], + [PresetDecorators.PROVIDE, [PresetDecorators.REGULAR]], + [PresetDecorators.CONSUME, [PresetDecorators.REGULAR]], + [PresetDecorators.STORAGE_PROP_REF, [PresetDecorators.REGULAR]], + [PresetDecorators.VARIABLE, [PresetDecorators.LINK]], + [PresetDecorators.LOCAL_STORAGE_LINK, [PresetDecorators.REGULAR]], +]); +// When there are multiple Decorators, filter out the Decorators that are not relevant to the rule +const decoratorsFilter: string[] = [ + PresetDecorators.PROVIDE, PresetDecorators.CONSUME, PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, PresetDecorators.BUILDER_PARAM, +]; + +class ConstructParameterRule extends AbstractUISyntaxRule { + private propertyMap: Map> = new Map(); + private regularVariableList: string[] = []; + private builderFunctionList: string[] = []; + + public setup(): Record { + return { + constructParameter: `The '{{initializer}}' property '{{initializerName}}' cannot be assigned to the '{{parameter}}' property '{{parameterName}}'.`, + initializerIsBuilder: `'@Builder' function '{{initializerName}}' can only initialize '@BuilderParam' attribute.`, + parameterIsBuilderParam: `'@BuilderParam' attribute '{{parameterName}}' can only initialized by '@Builder' function or '@Builder' method in struct.`, + }; + } + + public beforeTransform(): void { + this.propertyMap = new Map(); + this.regularVariableList = []; + this.builderFunctionList = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initList(node); + this.initPropertyMap(node); + this.checkConstructParameter(node); + } + + private getPropertyAnnotationName(node: arkts.AstNode, propertyName: string): string { + while (!arkts.isStructDeclaration(node)) { + if (!node.parent) { + return ''; + } + node = node.parent; + } + let annotationNames: string[] = []; + node.definition.body.forEach((item) => { + if (arkts.isClassProperty(item) && getClassPropertyName(item) === propertyName) { + annotationNames = getClassPropertyAnnotationNames(item); + } + if (arkts.isMethodDefinition(item) && getIdentifierName(item.name) === propertyName) { + annotationNames = item.scriptFunction.annotations.map((annotation) => + getAnnotationName(annotation) + ); + } + }); + if (annotationNames.length === 0) { + return PresetDecorators.REGULAR; + } + const annotationName = annotationNames.find((item) => { return decoratorsFilter.includes(item) }); + if (annotationName) { + return annotationName; + } + return ''; + } + + // Define a function to add property data to the property map + private addProperty( + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!this.propertyMap.has(structName)) { + this.propertyMap.set(structName, new Map()); + } + const structProperties = this.propertyMap.get(structName); + if (!structProperties) { + return; + } + structProperties.set(propertyName, annotationName); + } + + private collectBuilderFunctions(member: arkts.AstNode): void { + if (!arkts.isFunctionDeclaration(member) || !member.annotations) { + return; + } + member.annotations.forEach(annotation => { + if (annotation.expr && getIdentifierName(annotation.expr) === PresetDecorators.BUILDER && + member.scriptFunction.id) { + this.builderFunctionList.push(member.scriptFunction.id.name); + } + }); + } + + private collectRegularVariables(member: arkts.AstNode): void { + if (!arkts.isVariableDeclaration(member) || !member.declarators) { + return; + } + member.getChildren().forEach((item) => { + if (!arkts.isVariableDeclarator(item) || !item.name || + (item.initializer && arkts.isArrowFunctionExpression(item.initializer))) { + return; + } + this.regularVariableList.push(item.name.name); + }); + } + + private initList(node: arkts.AstNode): void { + // Record variables and functions that @builder decorate + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + this.collectBuilderFunctions(member); + this.collectRegularVariables(member); + }); + } + + private recordRestrictedDecorators( + item: arkts.AstNode, + structName: string, + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + let propertyName: string = getIdentifierName(item.key); + // If there is no decorator, it is a regular type + if (item.annotations.length === 0) { + let annotationName: string = PresetDecorators.REGULAR; + this.addProperty(structName, propertyName, annotationName); + } + // Iterate through the decorator of the property, and when the decorator is in the disallowAssignedDecorators array, the property is recorded + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (disallowAssignedDecorators.includes(annotationName)) { + this.addProperty(structName, propertyName, annotationName); + } + }); + } + + private initPropertyMap(node: arkts.AstNode): void { + // Iterate through the root node ahead of time, noting the structure name, variable name, and corresponding decorator type + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + if (structName === '') { + return; + } + member.definition?.body?.forEach((item) => { + this.recordRestrictedDecorators(item, structName); + }); + }); + } + + private reportRegularVariableError( + property: arkts.AstNode, + childType: string, + childName: string, + ): void { + if (!arkts.isProperty(property) || !property.value) { + return; + } + if (childType !== PresetDecorators.LINK) { + return; + } + if (arkts.isIdentifier(property.value) && this.regularVariableList.includes(property.value.name)) { + this.report({ + node: property, + message: this.messages.constructParameter, + data: { + initializer: PresetDecorators.REGULAR, + initializerName: property.value.name, + parameter: `@${childType}`, + parameterName: childName, + }, + }); + } + } + + private reportBuilderError( + property: arkts.AstNode, + childType: string, + childName: string, + ): void { + if (!arkts.isProperty(property) || !property.value) { + return; + } + let isBuilderInStruct: boolean = false; + if (arkts.isMemberExpression(property.value) && arkts.isIdentifier(property.value.property)) { + const parentName = property.value.property.name; + const parentType: string = this.getPropertyAnnotationName(property, parentName); + if (parentType === '') { + return; + } + isBuilderInStruct = parentType === PresetDecorators.BUILDER; + } + let isBuilder: boolean = false; + if (arkts.isIdentifier(property.value)) { + isBuilder = this.builderFunctionList.includes(property.value.name); + if (this.builderFunctionList.includes(property.value.name) && childType !== PresetDecorators.BUILDER_PARAM) { + this.report({ + node: property, + message: this.messages.initializerIsBuilder, + data: { + initializerName: property.value.name, + parameterName: childName, + }, + }); + } + } + if (childType === PresetDecorators.BUILDER_PARAM && !isBuilder && !isBuilderInStruct) { + this.report({ + node: property, + message: this.messages.parameterIsBuilderParam, + data: { + parameterName: childName, + }, + }); + } + } + + private checkConstructParameter(node: arkts.AstNode): void { + if (!arkts.isIdentifier(node) || !this.propertyMap.has(getIdentifierName(node))) { + return; + } + let structName: string = getIdentifierName(node); + if (!node.parent) { + return; + } + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // Gets all the properties recorded by the struct + const childPropertyName: Map = this.propertyMap.get(structName)!; + parentNode.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key) { + return; + } + const childName = getIdentifierName(property.key); + if (!childPropertyName.has(childName) || !property.value) { + return; + } + const childType: string = childPropertyName.get(childName)!; + this.reportRegularVariableError(property, childType, childName); + this.reportBuilderError(property, childType, childName); + if (!arkts.isMemberExpression(property.value) || !arkts.isThisExpression(property.value.object)) { + return; + } + const parentName = getIdentifierName(property.value.property); + const parentType: string = this.getPropertyAnnotationName(node, parentName); + if (parentType === '') { + return; + } + if (restrictedDecoratorInitializations.has(parentType) && + restrictedDecoratorInitializations.get(parentType)!.includes(childType)) { + this.report({ + node: property, + message: this.messages.constructParameter, + data: { + initializer: parentType === PresetDecorators.REGULAR ? PresetDecorators.REGULAR : `@${parentType}`, + initializerName: parentName, + parameter: childType === PresetDecorators.REGULAR ? PresetDecorators.REGULAR : `@${childType}`, + parameterName: childName, + }, + }); + } + }); + }); + } +} + +export default ConstructParameterRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts index cb4f7fcd8d2ca14ee0257e8f7424a9e82aec7755..538f7e9fcef5a83726f9f0c5ffb0ab0aa520490b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts @@ -14,258 +14,312 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, MultiMap, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -// Traverse the member variables of the struct, recording the members of the @Consumer modifications -function processStructMembers( - node: arkts.StructDeclaration, - structName: string, - componentv2WithConsumer: MultiMap -): void { - node.definition.body.forEach((member) => { - // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized - if (arkts.isClassProperty(member)) { - const memberName = member?.key?.dumpSrc(); - structName && memberName ? componentv2WithConsumer.add(structName, memberName) : null; - } - }); -} -function rememberStructName(node: arkts.AstNode, componentv2WithConsumer: MultiMap): void { - // First it has to be of the struct type - if (arkts.isStructDeclaration(node)) { - node?.definition?.annotations.forEach((anno) => { - // Second, it must be decorated with a @component v2 decorator - if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2) { - const structName = node.definition.ident?.name ?? ''; - processStructMembers(node, structName, componentv2WithConsumer); - } - }); - } -} -function findDecorator(member: arkts.ClassProperty, decorator: string): arkts.AnnotationUsage | undefined { - return member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decorator - ); -} -// Verify that the @Consumer decorator is used on the method -function validateConsumerOnMethod(member: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const annotationNode = member.scriptFunction.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.CONSUMER - ); - if (annotationNode) { - context.report({ - node: annotationNode, - message: rule.messages.consumerOnlyOnMember, - fix: (annotationNode) => { - const startPosition = arkts.getStartPosition(annotationNode); - const endPosition = arkts.getEndPosition(annotationNode); +import { getIdentifierName, MultiMap, PresetDecorators, getAnnotationName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { + private componentV2WithConsumer: MultiMap = new MultiMap(); + private componentV2WithProvider: MultiMap = new MultiMap(); + + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + providerAndConsumerOnlyOnProperty: `'@{{decorator}}' can only decorate member property.`, + multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in annotations.`, + providerAndConsumerOnlyInStruct: `The '@{{decorator}}' annotation can only be used with 'struct'.`, + forbiddenInitialization: `The '@{{decorator}}' property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, }; - } - }); - } -} -// @Consumer Bugs that conflict with other decorators -function reportMultipleBuiltInDecorators( - hasConsumeDecorator: arkts.AnnotationUsage, - otherDecorators: arkts.AnnotationUsage, - context: UISyntaxRuleContext, -): void { - context.report({ - node: hasConsumeDecorator, - message: rule.messages.multipleBuiltInDecorators, - fix: (hasConsumeDecorator) => { - const startPosition = arkts.getStartPosition(otherDecorators); - const endPosition = arkts.getEndPosition(otherDecorators); - return { - range: [startPosition, endPosition], - code: '', - }; } - }); -} -// Report a bug where @Provider is missing @ComponentV2 -function reportProviderRequiresComponentV2( - hasProviderDecorator: arkts.AnnotationUsage, - hasComponent: arkts.AnnotationUsage | undefined, - node: arkts.AstNode, - context: UISyntaxRuleContext, -): void { - if (hasComponent) { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerRequiresComponentV2, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(hasComponent); - const endPosition = arkts.getEndPosition(hasComponent); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}`, - }; - } - }); - } else { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerRequiresComponentV2, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = startPosition; - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}\n`, - }; - } - }); - } -} -// Verify decorator conflicts on member variables -function validateMemberDecorators(member: arkts.ClassProperty, - hasComponentV2: arkts.AnnotationUsage | undefined, - hasComponent: arkts.AnnotationUsage | undefined, - node: arkts.AstNode, - context: UISyntaxRuleContext -): void { - const hasConsumeDecorator = findDecorator(member, PresetDecorators.CONSUMER); - const hasProviderDecorator = findDecorator(member, PresetDecorators.PROVIDER); - const otherDecorators = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.CONSUMER - ); - if (hasConsumeDecorator && otherDecorators) { - reportMultipleBuiltInDecorators(hasConsumeDecorator, otherDecorators, context); - } - if (hasProviderDecorator && !hasComponentV2) { - reportProviderRequiresComponentV2(hasProviderDecorator, hasComponent, node, context); - } -} -// Verify that @Provider is being used in the class -function validateProviderInClass(member: arkts.ClassProperty, context: UISyntaxRuleContext): void { - const hasProviderDecorator = findDecorator(member, PresetDecorators.PROVIDER); - if (hasProviderDecorator) { - context.report({ - node: hasProviderDecorator, - message: rule.messages.providerOnlyInStruct, - fix: (hasProviderDecorator) => { - const startPosition = arkts.getStartPosition(hasProviderDecorator); - const endPosition = arkts.getEndPosition(hasProviderDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } -} -// Verify that the current identifier is an illegally initialized @Consumer member variable -function reportValidateConsumer( - currentNode: arkts.Identifier, - callExpName: string, - componentv2WithConsumer: MultiMap, - context: UISyntaxRuleContext -): void { - if (componentv2WithConsumer.get(callExpName).includes(currentNode.dumpSrc())) { - context.report({ - node: currentNode.parent, - message: rule.messages.forbiddenInitialization, - data: { - value: currentNode.dumpSrc(), - structName: callExpName - }, - fix: () => { - const startPosition = arkts.getStartPosition(currentNode.parent); - const endPosition = arkts.getEndPosition(currentNode.parent); - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } -} -// Verify that the @Consumer-decorated property is initialized -function validateConsumerInitialization(node: arkts.CallExpression, componentv2WithConsumer: MultiMap, - context: UISyntaxRuleContext): void { - const callExpName: string = node.expression.dumpSrc(); - if (componentv2WithConsumer.has(callExpName)) { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - reportValidateConsumer(currentNode, callExpName, componentv2WithConsumer, context); - } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); - } - } - } -} -function collectStructsWithConsumer(node: arkts.AstNode, componentv2WithConsumer: MultiMap): void { - // Used to document all V2 structs that use '@Consumer' - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - // Breadth traversal is done through while and queues - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - // Filter and record the nodes of the tree - rememberStructName(currentNode, componentv2WithConsumer); - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); - } + + public beforeTransform(): void { + this.componentV2WithConsumer = new MultiMap(); + this.componentV2WithProvider = new MultiMap(); } - } -} -function validateStructDecoratorsAndMembers(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.isStructDeclaration(node)) { - const hasComponentV2 = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const hasComponent = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - node.definition.body.forEach(member => { - if (arkts.isMethodDefinition(member)) { - validateConsumerOnMethod(member, context); - } - if (arkts.isClassProperty(member)) { - validateMemberDecorators(member, hasComponentV2, hasComponent, node, context); - } - }); - } -} -function validateProviderInClasses(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.isClassDeclaration(node)) { - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member)) { - validateProviderInClass(member, context); - } - }); - } -} -const rule: UISyntaxRule = { - name: 'consumer-provider-decorator-check', - messages: { - consumerOnlyOnMember: `'@Consumer' can only decorate member property.`, - multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in decorators.`, - providerRequiresComponentV2: `The '@Provider' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - providerOnlyInStruct: `The '@Provider' decorator can only be used with 'struct'.`, - forbiddenInitialization: `Property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, - }, - setup(context) { - // Used to record the names of the corresponding structs and member variables that are @consumer modified - let componentv2WithConsumer: MultiMap = new MultiMap(); - return { - parsed: (node): void => { - collectStructsWithConsumer(node, componentv2WithConsumer); - validateStructDecoratorsAndMembers(node, context); - validateProviderInClasses(node, context); + + public parsed(node: arkts.AstNode): void { + this.collectStructsWithConsumerAndProvider(node); + this.validateDecoratorsOnMember(node); + this.validateOnlyInStruct(node); + if (arkts.isCallExpression(node)) { - validateConsumerInitialization(node, componentv2WithConsumer, context); + this.validateConsumerInitialization(node); + this.validateProviderInitialization(node); } - }, - }; - }, -}; + } + + private collectStructsWithConsumerAndProvider(node: arkts.AstNode): void { + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + // Breadth traversal is done through while and queues + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + // Filter and record the nodes of the tree + this.rememberStructName(currentNode); + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private rememberStructName(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + // Second, it must be decorated with a @component v2 decorator + if (annoName === PresetDecorators.COMPONENT_V2) { + const structName = node.definition.ident?.name ?? ''; + this.processStructMembers(node, structName); + } + }); + } + } + + private processStructMembers(node: arkts.StructDeclaration, structName: string): void { + node.definition.body.forEach((member) => { + // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized + if (arkts.isClassProperty(member)) { + const consumerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.CONSUMER + ); + const providerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.PROVIDER + ); + if (!member.key) { + return; + } + const memberName = getIdentifierName(member.key); + if (consumerDecorator && structName && memberName) { + this.componentV2WithConsumer.add(structName, memberName); + } + + if (providerDecorator && structName && memberName) { + this.componentV2WithProvider.add(structName, memberName); + } + } + }); + } + + private validateDecoratorsOnMember(node: arkts.AstNode): void { + if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.PROVIDER); + } + + if (arkts.isStructDeclaration(node)) { + node.definition.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateMemberDecorators(member); + } + }); + } + } + + private validateMemberDecorators( + member: arkts.ClassProperty, + ): void { + // Check that the @Consumer is not mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.CONSUMER); + + // Check that the @Provider is mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.PROVIDER); + } + + private validateMultipleBuiltInDecorators(member: arkts.ClassProperty, decoratorName: string): void { + const decorator = this.findDecorator(member, decoratorName); + const otherDecorators = this.findOtherDecorator(member, decoratorName); + if (!decorator || !otherDecorators) { + return; + } + this.report({ + node: decorator, + message: this.messages.multipleBuiltInDecorators, + data: { + decorator: getAnnotationName(decorator) + }, + fix: () => { + let startPosition = otherDecorators.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = otherDecorators.endPosition; + return { + title: 'Remove other annotations', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private findDecorator( + member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string + ): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); + } + + private findOtherDecorator(member: arkts.ClassProperty, decoratorName: string): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== decoratorName + ); + } + + private validateOnlyInStruct(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + if (arkts.isMethodDefinition(member)) { + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + }); + return; + } + + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + return; + } + } + + private validateDecorator( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + message: string, + decoratorName: string + ): void { + const decorator = this.findDecorator(node, decoratorName); + if (!decorator) { + return; + } + + this.report({ + node: decorator, + message: message, + data: { + decorator: getAnnotationName(decorator), + }, + fix: (decorator) => { + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = decorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private validateConsumerInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; + } + const callExpName: string = node.expression.name; + if (this.componentV2WithConsumer.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidConsumerUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private validateProviderInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; + } + const callExpName: string = node.expression.name; + if (this.componentV2WithProvider.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidProviderUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + } + + private checkInvalidConsumerUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithConsumer.get(callExpName).includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.CONSUMER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + title: 'Remove the property', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + } + + private checkInvalidProviderUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithProvider.get(callExpName)?.includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.PROVIDER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + title: 'Remove the property', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + } +} -export default rule; +export default ConsumerProviderDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts b/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts index f7721812ca2cff340b1b259cc62cb30e1aa5c3a7..d35cf99f296b9971af47bdca044a45079d0b72c7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts @@ -14,55 +14,91 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyType, PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getClassPropertyType, PresetDecorators, getAnnotationUsage, isClassPropertyOptional } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const CUSTOM_DIALOG_CONTROLLER: string = 'CustomDialogController'; -function missingController( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - // Check for the @CustomDialog decorator - const hasCustomDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); - const structName = node.definition.ident; - if (!structName) { - return; +class CustomDialogMissingControllerRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingController: `The @CustomDialog decorated custom component must contain a property of the CustomDialogController type.`, + }; } - // Check if there is an attribute of type CustomDialogController in the class - let hasControllerProperty = false; - node.definition.body.forEach((property) => { - if (arkts.isClassProperty(property)) { - const propertyType = getClassPropertyType(property); - if (propertyType === CUSTOM_DIALOG_CONTROLLER) { - hasControllerProperty = true; - } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; } - }); - if (!hasControllerProperty && hasCustomDialogDecorator) { - context.report({ - node: structName, - message: rule.messages.missingController, - }); + this.checkMissingController(node); } -} -const rule: UISyntaxRule = { - name: 'custom-dialog-missing-controller', - messages: { - missingController: `The @CustomDialog decorated custom component must contain a property of the CustomDialogController type.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { + // Check if the @CustomDialog-decorated struct contains a property of type CustomDialogController + private checkMissingController(node: arkts.StructDeclaration): void { + const customDialogDecorator = getAnnotationUsage(node, PresetDecorators.CUSTOM_DIALOG); + + if (!customDialogDecorator) { + return; + } + + const structName = node.definition.ident; + if (!structName) { + return; + } + + let hasControllerProperty = false; + + node.definition.body.forEach((property) => { + if (arkts.isClassProperty(property)) { + // Check if it's a union type, such as CustomDialogController | undefined + if (this.hasCustomDialogControllerInUnion(property)) { + hasControllerProperty = true; return; } - missingController(node, context); - }, - }; - }, -}; -export default rule; + // Check if it's directly of the CustomDialogController type + const propertyType = getClassPropertyType(property); + if (propertyType === CUSTOM_DIALOG_CONTROLLER) { + hasControllerProperty = true; + } + } + }); + + if (!hasControllerProperty) { + this.report({ + node: structName, + message: this.messages.missingController, + }); + } + } + + // Check that the property is of a form that contains a CustomDialogController in the union type (for example: CustomDialogController | undefined) + private hasCustomDialogControllerInUnion(property: arkts.ClassProperty): boolean { + if (!isClassPropertyOptional(property)) { + return false; + } + + if (!property.typeAnnotation || !arkts.isETSUnionType(property.typeAnnotation)) { + return false; + } + + for (const type of property.typeAnnotation.types) { + if (!arkts.isETSTypeReference(type)) { + continue; + } + + const part = type.part; + if (!part || !arkts.isETSTypeReferencePart(part)) { + continue; + } + + const name = part.name; + if (name && arkts.isIdentifier(name) && name.name === CUSTOM_DIALOG_CONTROLLER) { + return true; + } + } + return false; + } +} +export default CustomDialogMissingControllerRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts b/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts deleted file mode 100644 index b01e0d40a1f5677f0ffb5a5e25cbe87dc665aa57..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 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 { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; - -// Helper function to find the '@Component' decorator in a ClassDeclaration and report errors. -function findComponentDecorator(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { - const componentDecorator = node.definition?.annotations?.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPONENT_V1 - ); - if (componentDecorator) { - reportDecoratorError(context, componentDecorator, rule.messages.invalidComponentDecorator); - } -} - -// Helper function to find the '@Prop' decorator in a MethodDefinition or ClassProperty. -const findPropDecorator = (node: arkts.MethodDefinition | arkts.ClassProperty): arkts.AnnotationUsage | undefined => { - const annotations = 'scriptFunction' in node ? node.scriptFunction.annotations : node.annotations; - return annotations?.find( - (annotation) => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.PROP - ); -}; - -// Rule 2: Check for '@Prop' on MethodDefinition -function checkPropOnMethod(context: UISyntaxRuleContext, node: arkts.MethodDefinition): void { - const propDecorator = findPropDecorator(node); - if (propDecorator) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); - } -}; - -// Rule 3: Check for '@Prop' on ClassProperty within a ClassDeclaration -function checkPropOnClassProperty(context: UISyntaxRuleContext, node: arkts.ClassProperty, currentNode: arkts.AstNode) - : void { - const propDecorator = findPropDecorator(node); - while (arkts.nodeType(currentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - currentNode = currentNode.parent; - if (propDecorator && arkts.isClassDeclaration(currentNode)) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); - } - } -}; - -function reportDecoratorError(context: UISyntaxRuleContext, Decorator: arkts.AnnotationUsage, message: string -): void { - context.report({ - node: Decorator, - message: message, - fix: () => { - const startPosition = arkts.getStartPosition(Decorator); - const endPosition = arkts.getEndPosition(Decorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} - -const rule: UISyntaxRule = { - name: 'no-prop-on-method', - messages: { - invalidComponentDecorator: `'@Component' can decorate only custom components.`, - propOnMethod: `'@Prop' can decorate only member variables of custom components.`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (arkts.isClassDeclaration(node)) { - findComponentDecorator(context, node); - } - if (arkts.isMethodDefinition(node)) { - checkPropOnMethod(context, node); - } - let currentNode = node; - if (arkts.isClassProperty(node)) { - checkPropOnClassProperty(context, node, currentNode); - } - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts index aa82f31044b738f69246d70bc95354f54a737801..7fb2892535ddafccfbf5fbb8d8cbc08c187d4586 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts @@ -15,47 +15,46 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkLocalStorageLink(node: arkts.StructDeclaration, context: UISyntaxRuleContext): void { - // Check if @Entry decorator exists with parameter - const entryDecorator = getAnnotationUsage(node, PresetDecorators.ENTRY); - const isStorageUsed = entryDecorator && node.definition?.annotations[0].properties[0]; - // Check if @LocalStorageLink exists - let localStorageLinkUsed = false; - node.definition.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; +class EntryLocalStorageCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + entryLocalStorageCheck: `'@Entry' should have a parameter, like '@Entry ({ storage: "__get_local_storage__" })'.`, + }; } - const propertyDecorators = getClassPropertyAnnotationNames(body); - localStorageLinkUsed = propertyDecorators.some( - decorator => decorator === PresetDecorators.LOCAL_STORAGE_LINK); - }); - // If @LocalStorageLink is used but @Entry(storage) is missing, report error - if (entryDecorator && localStorageLinkUsed && !isStorageUsed) { - context.report({ - node: entryDecorator, - message: rule.messages.invalidUsage - }); - } -} - -const rule: UISyntaxRule = { - name: 'entry-localstorage-check', - messages: { - invalidUsage: `@LocalStorageLink requires @Entry(storage) on the struct.`, - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + this.checkLocalStorageLink(node); + } + + private checkLocalStorageLink(node: arkts.StructDeclaration): void { + // Check if @Entry decorator exists with parameter + const entryDecorator = getAnnotationUsage(node, PresetDecorators.ENTRY); + const isStorageUsed = entryDecorator && entryDecorator.properties[0]; + // Check if @LocalStorageLink exists + let localStorageLinkUsed = false; + node.definition.body.forEach(body => { + if (!arkts.isClassProperty(body)) { + return; + } + const propertyDecorators = getClassPropertyAnnotationNames(body); + localStorageLinkUsed = propertyDecorators.some( + decorator => decorator === PresetDecorators.LOCAL_STORAGE_LINK || + decorator === PresetDecorators.STORAGE_PROP_REF); + }); + + // If @LocalStorageLink is used but @Entry(storage) is missing, report error + if (entryDecorator && localStorageLinkUsed && !isStorageUsed) { + this.report({ + node: entryDecorator, + message: this.messages.entryLocalStorageCheck + }); } - checkLocalStorageLink(node, context); - }, - }; - }, -}; + } +} -export default rule; +export default EntryLocalStorageCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts b/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts index 7017ab54f4e3b08dfb6f28480dd1e747746aa1af..0bd71b7c732f221a3c47bda827c3165e3d20080d 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts @@ -15,39 +15,38 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'entry-struct-no-export', - messages: { - noExportWithEntry: `It's not a recommended way to export struct with @Entry decorator, which may cause ACE Engine error in component preview mode.`, - }, - setup(context) { - return { - parsed: (node): void => { +class EntryStructNoExportRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noExportWithEntry: `It's not a recommended way to export struct with '@Entry' annotation, which may cause ACE Engine error in component preview mode.`, + }; + } + + public parsed(node: arkts.AstNode): void { // Check if the current node is a schema declaration if (!arkts.isStructDeclaration(node)) { - return; + return; } // Get the usage of the @Entry decorator const entryDecoratorUsage = getAnnotationUsage( - node, - PresetDecorators.ENTRY, + node, + PresetDecorators.ENTRY, ); //Determines whether the struct is exported - const isExported = node.dumpSrc().includes('export struct'); + const isStructExport = node.isExport; + const isStructDefaultExport = node.isDefaultExport; // If a @Entry decorator is present and the struct is exported - if (entryDecoratorUsage && isExported) { - context.report({ - node: entryDecoratorUsage, - message: this.messages.noExportWithEntry, - }); + if (entryDecoratorUsage && (isStructExport || isStructDefaultExport)) { + this.report({ + node: entryDecoratorUsage, + message: this.messages.noExportWithEntry, + }); } - }, - }; - }, + } }; -export default rule; \ No newline at end of file +export default EntryStructNoExportRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/index.ts b/arkui-plugins/ui-syntax-plugins/rules/index.ts index 13b008b627bb821cbb05603a977ad1238898fc87..6fdab381261947aa33f879e12306f57cf8f2acfb 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/index.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/index.ts @@ -13,87 +13,115 @@ * limitations under the License. */ -import { UISyntaxRule } from './ui-syntax-rule'; -import BuildRootNode from './build-root-node'; -import CheckConstructPrivateParameter from './check-construct-private-parameter'; -import CheckDecoratedPropertyType from './check-decorated-property-type'; -import ComponentComponentV2MixUseCheck from './component-componentV2-mix-use-check'; -import ComponentV2MixCheck from './componentV2-mix-check'; -import ConsumerProviderDecoratorCheck from './consumer-provider-decorator-check'; -import ComponentV2StateUsageValidation from './componentV2-state-usage-validation'; -import ConstructParameterLiteral from './construct-parameter-literal'; -import CustomDialogMissingController from './custom-dialog-missing-controller'; -import DecoratorsInUIComponentOnly from './decorators-in-ui-component-only'; -import EntryLoacalStorageCheck from './entry-localstorage-check'; -import EntryStructNoExport from './entry-struct-no-export'; -import LocalBuilderCheck from './local-builder-check'; -import MonitorDecoratorCheck from './monitor-decorator-check'; -import NestedRelationship from './nested-relationship'; -import NoChildInButton from './no-child-in-button'; -import NoDuplicateDecorators from './no-duplicate-decorators'; -import NoDuplicateEntry from './no-duplicate-entry'; -import NoDuplicatePreview from './no-duplicate-preview'; -import NoDuplicateStateManager from './no-duplicate-state-manager'; -import NoPropLinkObjectlinkInEntry from './no-prop-link-objectlink-in-entry'; -import NoSameAsBuiltInAttribute from './no-same-as-built-in-attribute'; -import ReuseAttributeCheck from './reuse-attribute-check'; -import StructMissingDecorator from './struct-missing-decorator'; -import StructPropertyDecorator from './struct-property-decorator'; -import StructVariableInitialization from './struct-variable-initialization'; -import TrackDecoratorCheck from './track-decorator-check'; -import TypeDecoratorCheck from './type-decorator-check'; -import ValidateBuildInStruct from './validate-build-in-struct'; -import VariableInitializationViaComponentCons from './variable-initialization-via-component-cons'; -import WatchDecoratorFunction from './watch-decorator-function'; -import WatchDecoratorRegular from './watch-decorator-regular'; -import WrapBuilderCheck from './wrap-builder-check'; -import ObservedHeritageCompatibleCheck from './observed-heritage-compatible-check'; -import ObservedObservedV2 from './observed-observedV2-check'; -import ObservedV2TraceUsageValidation from './observedV2-trace-usage-validation'; -import OnceDecoratorCheck from './once-decorator-check'; -import OneDecoratorOnFunctionMethod from './one-decorator-on-function-method'; -import OldNewDecoratorMixUseCheck from './old-new-decorator-mix-use-check'; +import { UISyntaxRule, UISyntaxRuleConfig } from './ui-syntax-rule'; +import BuilderParamDecoratorCheckRule from './builderparam-decorator-check'; +import AttributeNoInvokeRule from './attribute-no-invoke'; +import BuildRootNodeRule from './build-root-node'; +import CheckConstructPrivateParameterRule from './check-construct-private-parameter'; +import CheckDecoratedPropertyTypeRule from './check-decorated-property-type'; +import CheckPropertyModifiersRule from './check-property-modifiers'; +import ComponentComponentV2MixUseCheckRule from './component-componentV2-mix-use-check'; +import ComponentV2MixCheckRule from './componentV2-mix-check'; +import ConstructParameterLiteralRule from './construct-parameter-literal'; +import ConstructParameterRule from './construct-parameter'; +import ConsumerProviderDecoratorCheckRule from './consumer-provider-decorator-check'; +import ComputedDecoratorCheckRule from './computed-decorator-check'; +import ComponentV2StateUsageValidationRule from './componentV2-state-usage-validation'; +import CustomDialogMissingControllerRule from './custom-dialog-missing-controller'; +import EntryLocalStorageCheckRule from './entry-localstorage-check'; +import EntryStructNoExportRule from './entry-struct-no-export'; +import MainPagesEntryCheckRule from './main-pages-entry-check'; +import MonitorDecoratorCheckRule from './monitor-decorator-check'; +import NestedRelationshipRule from './nested-relationship'; +import NestedReuseComponentCheckRule from './nested-reuse-component-check'; +import NoChildInButtonRule from './no-child-in-button'; +import NoDuplicateEntryRule from './no-duplicate-entry'; +import NoDuplicateIdRule from './no-duplicate-id'; +import NoDuplicatePreviewRule from './no-duplicate-preview'; +import NoPropLinkObjectLinkInEntryRule from './no-prop-link-objectlink-in-entry'; +import NoSameAsBuiltInAttributeRule from './no-same-as-built-in-attribute'; +import ReuseAttributeCheckRule from './reuse-attribute-check'; +import StaticParamRequireRule from './static-param-require'; +import StructMissingDecoratorRule from './struct-missing-decorator'; +import StructPropertyDecoratorRule from './struct-property-decorator'; +import TrackDecoratorCheckRule from './track-decorator-check'; +import ValidateBuildInStructRule from './validate-build-in-struct'; +import WrapBuilderCheckRule from './wrap-builder-check'; +import StructPropertyOptionalRule from './struct-property-optional'; +import StructVariableInitializationRule from './struct-variable-initialization'; +import UiConsistentCheckRule from './ui-consistent-check'; +import ValidateDecoratorTargetRule from './validate-decorator-target'; +import WatchDecoratorFunctionRule from './watch-decorator-function'; +import WatchDecoratorRegularRule from './watch-decorator-regular'; +import ObservedHeritageCompatibleCheckRule from './observed-heritage-compatible-check'; +import ObservedObservedV2Rule from './observed-observedV2-check'; +import ObservedV2TraceUsageValidationRule from './observedV2-trace-usage-validation'; +import OnceDecoratorCheckRule from './once-decorator-check'; +import OneDecoratorOnFunctionMethodRule from './one-decorator-on-function-method'; +import PropertyTypeRule from './property-type'; +import ReusableV2DecoratorCheckRule from './reusableV2-decorator-check'; +import VariableInitializationViaComponentConstructorRule from './variable-initialization-via-component-constructor'; +import ComponentComponentV2InitCheckRule from './component-componentV2-init-check'; +import StructNoExtendsRule from './struct-no-extends'; +import OldNewDecoratorMixUseCheckRule from './old-new-decorator-mix-use-check'; +import RequireDecoratorRegularRule from './require-decorator-regular'; +import ReusableComponentInV2CheckRule from './reusable-component-in-V2-check'; +import SpecificComponentChildrenRule from './specific-component-children'; -const rules: UISyntaxRule[] = [ - BuildRootNode, - CheckConstructPrivateParameter, - CheckDecoratedPropertyType, - ComponentComponentV2MixUseCheck, - ComponentV2MixCheck, - ConsumerProviderDecoratorCheck, - ComponentV2StateUsageValidation, - ConstructParameterLiteral, - CustomDialogMissingController, - DecoratorsInUIComponentOnly, - EntryLoacalStorageCheck, - EntryStructNoExport, - LocalBuilderCheck, - MonitorDecoratorCheck, - NestedRelationship, - NoChildInButton, - NoDuplicateDecorators, - NoDuplicateEntry, - NoDuplicatePreview, - NoDuplicateStateManager, - NoPropLinkObjectlinkInEntry, - NoSameAsBuiltInAttribute, - ReuseAttributeCheck, - StructMissingDecorator, - StructPropertyDecorator, - StructVariableInitialization, - TrackDecoratorCheck, - TypeDecoratorCheck, - ValidateBuildInStruct, - VariableInitializationViaComponentCons, - WatchDecoratorFunction, - WatchDecoratorRegular, - WrapBuilderCheck, - ObservedHeritageCompatibleCheck, - ObservedObservedV2, - ObservedV2TraceUsageValidation, - OnceDecoratorCheck, - OneDecoratorOnFunctionMethod, - OldNewDecoratorMixUseCheck, +const rules: Array = [ + [AttributeNoInvokeRule, 'warn'], + [BuildRootNodeRule, 'error'], + [BuilderParamDecoratorCheckRule, 'error'], + [CheckConstructPrivateParameterRule, 'warn'], + [CheckDecoratedPropertyTypeRule, 'error'], + [CheckPropertyModifiersRule, 'warn'], + [ComponentComponentV2MixUseCheckRule, 'error'], + [ComponentV2MixCheckRule, 'error'], + [ConstructParameterLiteralRule, 'warn'], + [ConstructParameterRule, 'error'], + [ConsumerProviderDecoratorCheckRule, 'error'], + [ComponentV2StateUsageValidationRule, 'error'], + [CustomDialogMissingControllerRule, 'error'], + [EntryLocalStorageCheckRule, 'warn'], + [EntryStructNoExportRule, 'warn'], + [MainPagesEntryCheckRule, 'error'], + [MonitorDecoratorCheckRule, 'error'], + [NestedRelationshipRule, 'error'], + [NestedReuseComponentCheckRule, 'error'], + [NoChildInButtonRule, 'error'], + [NoDuplicateEntryRule, 'error'], + [NoDuplicateIdRule, 'warn'], + [NoDuplicatePreviewRule, 'error'], + [NoPropLinkObjectLinkInEntryRule, 'warn'], + [NoSameAsBuiltInAttributeRule, 'error'], + [ReuseAttributeCheckRule, 'error'], + [StaticParamRequireRule, 'warn'], + [StructMissingDecoratorRule, 'error'], + [StructPropertyDecoratorRule, 'error'], + [TrackDecoratorCheckRule, 'error'], + [ValidateBuildInStructRule, 'error'], + [WrapBuilderCheckRule, 'error'], + [StructPropertyOptionalRule, 'warn'], + [StructVariableInitializationRule, 'error'], + [ObservedHeritageCompatibleCheckRule, 'error'], + [ObservedObservedV2Rule, 'error'], + [ObservedV2TraceUsageValidationRule, 'error'], + [OnceDecoratorCheckRule, 'error'], + [OneDecoratorOnFunctionMethodRule, 'error'], + [PropertyTypeRule, 'error'], + [ComputedDecoratorCheckRule, 'error'], + [ComponentComponentV2InitCheckRule, 'error'], + [ReusableV2DecoratorCheckRule, 'error'], + [VariableInitializationViaComponentConstructorRule, 'error'], + [StructNoExtendsRule, 'error'], + [UiConsistentCheckRule, 'warn'], + [ValidateDecoratorTargetRule, 'error'], + [WatchDecoratorFunctionRule, 'error'], + [WatchDecoratorRegularRule, 'error'], + [OldNewDecoratorMixUseCheckRule, 'error'], + [RequireDecoratorRegularRule, 'warn'], + [ReusableComponentInV2CheckRule, 'warn'], + [SpecificComponentChildrenRule, 'error'], ]; -export default rules; +export default rules; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts b/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..c0b9181e4a4de46ff022adfd40d0b7db2bfb62e6 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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 { getAnnotationUsage, getCurrentFilePath, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class MainPagesEntryCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + mainPagesEntryCheck: `A page configured in 'main_pages. json or build-profile. json5' must have one and only one '@Entry' annotation. ` + }; + } + public parsed(node: arkts.AstNode): void { + if (!arkts.isEtsScript(node) || node.isNamespace) { + return; + } + const currentFilePath = getCurrentFilePath(node); + if (!currentFilePath) { + return; + } + if (!this.context.getMainPages().includes(currentFilePath)) { + return; + } + let entryDecoratorCount = 0; + // Store the first StructDeclaration + let firstStructDeclaration: arkts.AstNode | undefined = undefined; + // Traverse all child nodes of the Program + for (const child of node.getChildren()) { + // Check if it's of type StructDeclaration + if (arkts.isStructDeclaration(child)) { + if (!firstStructDeclaration) { + firstStructDeclaration = child.definition.ident; + } + const entryDocoratorUsage = getAnnotationUsage( + child, + PresetDecorators.ENTRY, + ); + if (entryDocoratorUsage) { + ++entryDecoratorCount; + } + } + } + if (entryDecoratorCount === 0) { + this.report({ + node: firstStructDeclaration ? firstStructDeclaration : node, + message: this.messages.mainPagesEntryCheck, + }); + } + } +} + +export default MainPagesEntryCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts index e0696de480d387ca451ee31ccd429e29d9547250..e845cac4430e3780f6f039edf407cdbe276b9f32 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -14,161 +14,216 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Function declarations moved to the top with explicit return types -function getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { - const localMonitorUsed = body.scriptFunction.annotations?.find( - annotation => annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.MONITOR - ); - return localMonitorUsed; -} +import { getAnnotationUsage, PresetDecorators, getClassAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkConflictingDecorators(context: UISyntaxRuleContext, body: arkts.MethodDefinition, - localMonitorUsed: arkts.AnnotationUsage): boolean { - const conflictingDecorators = body.scriptFunction.annotations?.filter( - annotation => annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.MONITOR - ); - if (conflictingDecorators?.length > 0) { - reportConflictingDecorators(context, localMonitorUsed, conflictingDecorators); - return true; - } - return false; -} +class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + monitorUsedAlone: + `The member property or method can not be decorated by multiple built-in annotations.`, + monitorUsedInObservedV2Class: + `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, + monitorUsedInComponentV2Struct: + `The '@Monitor' annotation can only be used in a 'struct' decorated with '@ComponentV2'.`, + monitorDecorateMethod: + `@Monitor can only decorate method.` + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { + return; + } + + const monitorDecorator = this.checkMonitorUsage(node); + if (monitorDecorator && arkts.isClassDeclaration(node)) { + this.checkMonitorInClass(node, monitorDecorator); + } -function reportConflictingDecorators(context: UISyntaxRuleContext, localMonitorUsed: arkts.AstNode, - conflictingDecorators: arkts.AnnotationUsage[]): void { - context.report({ - node: localMonitorUsed, - message: rule.messages.invalidUsage1, - fix: () => { - const startPositions = conflictingDecorators.map(annotation => - arkts.getStartPosition(annotation)); - const endPositions = conflictingDecorators.map(annotation => arkts.getEndPosition(annotation)); - const startPosition = startPositions[0]; - const endPosition = endPositions[endPositions.length - 1]; - return { - range: [startPosition, endPosition], - code: '' - }; + if (monitorDecorator && arkts.isStructDeclaration(node)) { + this.checkMonitorInStruct(node, monitorDecorator); + } + this.checkDecorateMethod(node); } - }); -} -function checkIfClassIsObservedV2(node: arkts.ClassDeclaration): boolean { - return node.definition?.annotations?.some( - observedV2 => observedV2.expr?.dumpSrc() === PresetDecorators.OBSERVED_V2 - ) ?? false; -} -function checkIfStructIsComponentV2(node: arkts.StructDeclaration): boolean { - return node.definition?.annotations?.some( - componentV2 => componentV2.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2 - ) ?? false; -} + private checkMonitorInClass( + node: arkts.ClassDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const isObservedV2 = this.checkDecorator(node, PresetDecorators.OBSERVED_V2); + const observedV1Decorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V1); -function reportInvalidUsage(context: UISyntaxRuleContext, node: arkts.AstNode, message: string, fixCode: string) - : void { - const startPosition = arkts.getStartPosition(node); - context.report({ - node, - message, - fix: () => ({ - range: [startPosition, startPosition], - code: fixCode, - }), - }); -} + if (!isObservedV2 && !observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + return { + title: 'Add @ObservedV2 annotation', + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n` + }; + } + }); + return; + } + if (!isObservedV2 && observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + return { + title: 'Change @Observed to @ObservedV2', + range: [observedV1Decorator.startPosition, observedV1Decorator.endPosition], + code: `${PresetDecorators.OBSERVED_V2}` + }; + } + }); + } + } + + private checkMonitorInStruct( + node: arkts.StructDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const componentV1Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + const isComponentV2 = this.checkDecorator(node, PresetDecorators.COMPONENT_V2); + if (!isComponentV2 && !componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => ({ + title: 'Add @ComponentV2 annotation', + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n` + }) + }); + return; + } -function checkMultipleDecorators( - node: arkts.ClassDeclaration | arkts.StructDeclaration, - context: UISyntaxRuleContext -): boolean { - // Traverse body of the class to check for @Monitor usage - let monitorUsed: boolean = false; - node.definition?.body.forEach(body => { - if (arkts.isMethodDefinition(body)) { - const localMonitorUsed = getLocalMonitorUsed(body); - if (localMonitorUsed) { - monitorUsed = true; - checkConflictingDecorators(context, body, localMonitorUsed); - return; // Stop further checks for this method - } + if (!isComponentV2 && componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => { + return { + title: 'Change @Component to @ComponentV2', + range: [componentV1Decorator.startPosition, componentV1Decorator.endPosition], + code: `${PresetDecorators.COMPONENT_V2}` + }; + } + }); + } } - }); - return monitorUsed; -} -function checkDecorateMethod( - node: arkts.ClassDeclaration | arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - // Check if @Monitor is used on a property (which is not allowed) - node.definition?.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; + private checkDecorator(node: arkts.ClassDeclaration | arkts.StructDeclaration, decoratorName: string): boolean { + return node.definition?.annotations?.some( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr?.name === decoratorName + ) ?? false; } - const monitorDecorator = body.annotations?.find( - annotation => annotation.expr?.dumpSrc() === PresetDecorators.MONITOR); - if (monitorDecorator === undefined) { - return; + + private checkMonitorUsage( + node: arkts.ClassDeclaration | arkts.StructDeclaration + ): arkts.AnnotationUsage | undefined { + let monitorUsage: arkts.AnnotationUsage | undefined; + + for (const body of node.definition?.body ?? []) { + if (!arkts.isMethodDefinition(body)) { + continue; + } + const currentMonitor = this.getLocalMonitorUsed(body); + + if (currentMonitor) { + monitorUsage = currentMonitor; + this.checkConflictingDecorators(body, currentMonitor); + break; + } + } + return monitorUsage; } - context.report({ - node: monitorDecorator, - message: rule.messages.invalidUsage4, - fix: () => { - const startPosition = arkts.getStartPosition(monitorDecorator); - const endPosition = arkts.getEndPosition(monitorDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - }); -} -// The rule object with its setup method -const rule: UISyntaxRule = { - name: 'monitor-decorator-check', - messages: { - invalidUsage1: - `The member property or method can not be decorated by multiple built-in decorators.`, - invalidUsage2: - `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, - invalidUsage3: - `The '@Monitor' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - invalidUsage4: - `@Monitor can only decorate method`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { - return; + private getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { + const localMonitorUsed = body.scriptFunction.annotations?.find( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + return localMonitorUsed; + } + + private checkConflictingDecorators(body: arkts.MethodDefinition, localMonitorUsed: arkts.AnnotationUsage): boolean { + const conflictingDecorators = body.scriptFunction.annotations?.filter( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.MONITOR + ); + if (conflictingDecorators?.length > 0) { + this.reportConflictingDecorators(localMonitorUsed, conflictingDecorators); + return true; } - let monitorUsed = false; + return false; + } - const isObservedV2 = arkts.isClassDeclaration(node) && checkIfClassIsObservedV2(node); - const isComponentV2 = arkts.isStructDeclaration(node) && checkIfStructIsComponentV2(node); + private reportConflictingDecorators(localMonitorUsed: arkts.AstNode, conflictingDecorators: arkts.AnnotationUsage[]): void { + this.report({ + node: localMonitorUsed, + message: this.messages.monitorUsedAlone, + fix: () => { + const startPositions = conflictingDecorators.map(annotation => + annotation.startPosition); + const endPositions = conflictingDecorators.map(annotation => annotation.endPosition); + let startPosition = startPositions[0]; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = endPositions[endPositions.length - 1]; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '' + }; + } + }); + } - monitorUsed = checkMultipleDecorators(node, context); + private checkDecorateMethod(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { + // Check if @Monitor is used on a property (which is not allowed) + node.definition?.body.forEach((body) => { + if (!arkts.isClassProperty(body)) { + return; + } - // Check for errors related to @Monitor usage - if (monitorUsed && !isObservedV2 && arkts.isClassDeclaration(node)) { - reportInvalidUsage(context, node, rule.messages.invalidUsage2, `@${PresetDecorators.OBSERVED_V2}\n`); - } - if (monitorUsed && !isComponentV2 && arkts.isStructDeclaration(node)) { - reportInvalidUsage(context, node, rule.messages.invalidUsage3, `@${PresetDecorators.COMPONENT_V2}\n`); - } + const monitorDecorator = body.annotations?.find( + (annotation) => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); - checkDecorateMethod(node, context); - }, - }; - }, -}; + if (monitorDecorator === undefined) { + return; + } + this.report({ + node: monitorDecorator, + message: this.messages.monitorDecorateMethod, + fix: () => { + let startPosition = monitorDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = monitorDecorator.endPosition; + return { + title: 'Remove the @Monitor annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + }); + } +} -export default rule; +export default MonitorDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts b/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts index fd18100e9be82ade14f15b2b746c4cf718f3237b..2f4ee91e01762d81c4c699c03f828bd941691d48 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.ts @@ -14,178 +14,185 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { + getIdentifierName, + isAtomicComponent, + isBuildInComponent, + isSingleChildComponent, + listToString, + SINGLE_CHILD_COMPONENT +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkInvalidChildInText(node: arkts.AstNode, context: UISyntaxRuleContext, textChild: string[]): void { - // Check if the current node is an identifier, and name is 'Text' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Text') { - return; - } - if (!node.parent) { - return; - } - const parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If the BlockStatement contains a child component that should not exist under the text, an error will be reported - parentNode.getChildren().forEach(member => { - if (!arkts.isBlockStatement(member)) { - return; +class NestedRelationshipRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noChildComponent: `The component '{{componentName}}' can't have any child.`, + singleChildComponent: `The '{{componentName}}' component can have only one child component.`, + delegateChildrenComponentChildren: `The '{{childComponentName}}' component cannot be a child component of the {{componentName}} component.`, + delegateChildrenComponentParent: `The component '{{parentComponentName}}' can only have the child component '{{childComponentList}}'.`, + delegateParentComponent: `The '{{componentName}}' component can only be nested in the '{{parentComponentList}}' parent component.`, + }; } - member.getChildren().forEach(sibling => { - if (!arkts.isExpressionStatement(sibling) || !arkts.isCallExpression(sibling.expression)) { - return; - } - const childComponentName = sibling.expression.expression.dumpSrc(); - if (!textChild.includes(childComponentName)) { - context.report({ - node: node, - message: rule.messages.invalidChildInText - }); - return; - } - }); - }); -} -function checkOneChildInButton(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'Button' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Button') { - return; - } - if (!node.parent) { - return; - } - const parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If there is more than one subcomponent in the BlockStatement, an error is reported - parentNode.getChildren().forEach(member => { - if (!arkts.isBlockStatement(member)) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.checkValidParentComponent(node); + this.checkValidChildComponent(node); + this.checkSingleChildComponent(node); + this.checkNoChildComponent(node); } - if (member.statements.length > 1) { - context.report({ - node: node, - message: rule.messages.oneChildInButton - }); - } - }); -} -function checkListItem(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'ListItem' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'ListItem') { - return; - } - if (!node.parent || !node.parent.parent) { - return; - } - let curNode: arkts.AstNode = node.parent.parent; - do { - while (!arkts.isCallExpression(curNode)) { - if (!curNode.parent) { - return; - } - curNode = curNode.parent; + private checkValidParentComponent(node: arkts.AstNode): void { + // Check if the node is an identifier and if there are any restrictions on the parent component + if (!arkts.isIdentifier(node) || !this.context.componentsInfo) { + return; + } + const componentName: string = getIdentifierName(node); + if (!this.context.componentsInfo.validParentComponent.has(componentName) || !node.parent || !node.parent.parent) { + return; + } + let curNode = node.parent.parent; + while (!arkts.isCallExpression(curNode) || !arkts.isIdentifier(curNode.expression) || + !isBuildInComponent(this.context, curNode.expression.name)) { + if (!curNode.parent) { + return; + } + curNode = curNode.parent; + } + // If the parent component of the current component is not within the valid range, an error is reported + const parentComponentName = curNode.expression.name; + if (!this.context.componentsInfo.validParentComponent.get(componentName)!.includes(parentComponentName)) { + const parentComponentListArray: string[] = this.context.componentsInfo.validParentComponent.get(componentName)!; + this.report({ + node: node, + message: this.messages.delegateParentComponent, + data: { + componentName: componentName, + parentComponentList: listToString(parentComponentListArray), + }, + }); + } } - const parentName: string = curNode.expression.dumpSrc(); - if (parentName === 'List') { // If the parent component's name is 'List', exit directly - break; - } else if (parentName !== 'ForEach') { // If the parent component's name is not 'List' or 'ForEach', throw an error - context.report({ - node: node, - message: rule.messages.listItemCannotInOther, - data: { parentName: parentName } - }); - context.report({ - node: node, - message: rule.messages.listItemMustInList - }); - break; + + private checkValidChildComponent(node: arkts.AstNode): void { + // Check whether the node is an identifier and whether there are restrictions on subcomponents + if (!arkts.isIdentifier(node) || !this.context.componentsInfo) { + return; + } + const componentName: string = getIdentifierName(node); + if (!this.context.componentsInfo.validChildComponent.has(componentName) || !node.parent || + !arkts.isCallExpression(node.parent) || !arkts.isIdentifier(node.parent.expression)) { + return; + } + let parentNode = node.parent; + const childComponentListArray = this.context.componentsInfo.validChildComponent.get(componentName); + if (!childComponentListArray) { + return; + } + let reportFlag: boolean = false; + // If the BlockStatement contains a child component that should not exist under the component, an error will be reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + member.statements.forEach(statement => { + if (!arkts.isExpressionStatement(statement) || !statement.expression || + !arkts.isCallExpression(statement.expression) || !statement.expression.expression || + !arkts.isIdentifier(statement.expression.expression)) { + return; + } + const childComponentNode = statement.expression.expression; + const childComponentName = getIdentifierName(childComponentNode); + if (childComponentListArray.includes(childComponentName) || + !isBuildInComponent(this.context, childComponentName)) { + return; + } + reportFlag = true; + this.reportDelegateChildrenComponentChildren(childComponentNode, childComponentName, componentName); + }); + }); + if (!reportFlag) { + return; + } + this.report({ + node: node, + message: this.messages.delegateChildrenComponentParent, + data: { + parentComponentName: componentName, + childComponentList: listToString(childComponentListArray), + }, + }); } - // In the remaining case, the parent component is 'ForEach', continue traversing upwards for further checks - if (!curNode.parent) { - return; + + private reportDelegateChildrenComponentChildren( + parentNode: arkts.Identifier, + childComponentName: string, + componentName: string + ): void { + this.report({ + node: parentNode, + message: this.messages.delegateChildrenComponentChildren, + data: { + childComponentName: childComponentName, + componentName: componentName, + }, + }); } - curNode = curNode.parent; - } while (true); -} -function checkSpan(node: arkts.AstNode, context: UISyntaxRuleContext): void { - // Check if the current node is an identifier, and name is 'Span' - if (!arkts.isIdentifier(node)) { - return; - } - if (getIdentifierName(node) !== 'Span') { - return; - } - let parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // If there are subcomponents in the BlockStatement, an error is reported - parentNode.getChildren().forEach(sibling => { - if (!arkts.isBlockStatement(sibling)) { - return; + private checkSingleChildComponent(node: arkts.AstNode): void { + // Check whether the current node is an identifier and a single subcomponent container + if (!arkts.isIdentifier(node)) { + return; + } + const componentName: string = getIdentifierName(node); + if (!isSingleChildComponent(this.context, componentName) || !node.parent) { + return; + } + const parentNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // If there is more than one subcomponent in the BlockStatement, an error is reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + if (member.statements.length > SINGLE_CHILD_COMPONENT) { + this.report({ + node: node, + message: this.messages.singleChildComponent, + data: { componentName: componentName } + }); + } + }); } - if (sibling.statements.length > 0) { - context.report({ - node: node, - message: rule.messages.noChildInSpan - }); + + private checkNoChildComponent(node: arkts.AstNode): void { + // Check whether the current node is an identifier and an atomic component + if (!arkts.isIdentifier(node)) { + return; + } + const componentName: string = getIdentifierName(node); + if (!isAtomicComponent(this.context, componentName) || !node.parent) { + return; + } + let parentNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // If there are child components in arguments, an error will be reported + parentNode.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + if (member.statements.length > 0) { + this.report({ + node: node, + message: this.messages.noChildComponent, + data: { componentName: componentName } + }); + } + }); } - }); - if (!node.parent || !node.parent.parent) { - return; - } - parentNode = parentNode.parent; - while (!arkts.isCallExpression(parentNode)) { - parentNode = parentNode.parent; - } - const parentName: string = parentNode.expression.dumpSrc(); - // If the name of the parent component is not 'Text', an error is reported - if (parentName !== 'Text' && parentName !== 'RichEditor' && parentName !== 'ContainerSpan') { - context.report({ - node: node, - message: rule.messages.spanMustInText - }); - } } -// The 'Text' component can have only the Span, ImageSpan, ContainerSpan and SymbolSpan child component. - -const rule: UISyntaxRule = { - name: 'nested-relationship', - messages: { - invalidChildInText: `The 'Text' component can have only the Span, ImageSpan, ContainerSpan and SymbolSpan child component.`, - oneChildInButton: `The 'Button' component can have only one child component.`, - listItemMustInList: `The 'ListItem' component can only be nested in the List and ListItemGroup parent component.`, - listItemCannotInOther: `The 'ListItem' component cannot be a child component of the '{{parentName}}' component.`, - noChildInSpan: `No child component is allowed in the 'Span' component. `, - spanMustInText: `The 'Span' component can only be nested in the Text, RichEditor and ContainerSpan parent component. `, - }, - setup(context) { - const textChild: string[] = ['Span', 'ImageSpan', 'ContainerSpan', 'SymbolSpan']; - return { - parsed: (node): void => { - checkInvalidChildInText(node, context, textChild); - checkOneChildInButton(node, context); - checkListItem(node, context); - checkSpan(node, context); - }, - }; - }, -}; -export default rule; \ No newline at end of file +export default NestedRelationshipRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1206b1ab177f28fd07423ceefa91c85446508fb --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts @@ -0,0 +1,201 @@ +/* + * Copyright (c) 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 { COMPONENT_REPEAT, getIdentifierName, PresetDecorators, TEMPLATE } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class NestedReuseComponentCheckRule extends AbstractUISyntaxRule { + private reusableV2StructName: string[] = []; + private reusableStructName: string[] = []; + + public setup(): Record { + return { + noReusableV2InComponentV1: `A custom component decorated with @Component cannot contain child components decorated with @ReusableV2.`, + noReusableV2InReusableV1: `A custom component decorated with @Reusable cannot contain child components decorated with @ReusableV2.`, + noReusableV1InReusableV2: `A custom component decorated with @ReusableV2 cannot contain child components decorated with @Reusable.`, + noReusableV2InRepeatTemplate: `The template attribute of the Repeat component cannot contain any custom component decorated with @ReusableV2.`, + }; + } + + public beforeTransform(): void { + this.reusableV2StructName = []; + this.reusableStructName = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.initStructName(node); + this.checkNestedReuseComponent(node); + this.checkNoReusableV1InReusableV2(node); + } + + private initStructName(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + // Get a list of annotations + const annotationsList = childNode.definition.annotations; + // Check that the current component has @Reusable and @ReusableV2 decorators + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableV2StructName.push(struceName); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableStructName.push(struceName); + } + } + } + + private reportNoReusableV2InRepeatTemplate(errorNode: arkts.AstNode): void { + this.report({ + node: errorNode, + message: this.messages.noReusableV2InRepeatTemplate, + fix: (errorNode) => { + return { + title: 'Remove the component', + range: [errorNode.startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } + + private checkHasRepeatOrTemplate(node: arkts.CallExpression): { hasRepeat: boolean, hasTemplate: boolean } { + let hasRepeat: boolean = false; + let hasTemplate: boolean = false; + while (arkts.isCallExpression(node) && + node.expression && arkts.isMemberExpression(node.expression) && + node.expression.object && arkts.isCallExpression(node.expression.object)) { + if (arkts.isIdentifier(node.expression.property) && getIdentifierName(node.expression.property) === TEMPLATE) { + hasTemplate = true; + } + node = node.expression.object; + } + if (arkts.isCallExpression(node) && arkts.isIdentifier(node.expression)) { + hasRepeat = getIdentifierName(node.expression) === COMPONENT_REPEAT; + } + return { hasRepeat, hasTemplate }; + } + + private checkNoReusableV2InRepeatTemplate(node: arkts.AstNode, errorNode: arkts.AstNode): boolean { + if (!arkts.isCallExpression(node)) { + return false; + } + const flag = this.checkHasRepeatOrTemplate(node); + if (!flag.hasRepeat || !flag.hasTemplate) { + return false; + } + this.reportNoReusableV2InRepeatTemplate(errorNode); + return true; + } + + private reportNoReusableV1InReusableV2(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV1InReusableV2, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private checkNoReusableV1InReusableV2(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableStructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + } + const annotationsList = struceNode.definition.annotations; + // Check that the current component is decorated by the @ComponentV2 decorator + if (annotationsList.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + this.reportNoReusableV1InReusableV2(node); + } + } + } + + private reportNoReusableV2InReusableV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InReusableV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private reportNoReusableV2InComponentV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InComponentV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private checkNestedReuseComponent(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableV2StructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + let hasReportedError = false; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + if (!hasReportedError) { + hasReportedError = this.checkNoReusableV2InRepeatTemplate(struceNode, node); + } + } + // Gets a list of decorators for the current Struct + const annotationsList = struceNode.definition.annotations; + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + this.reportNoReusableV2InReusableV1(node); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V1)) { + this.reportNoReusableV2InComponentV1(node); + } + } + } +} + +export default NestedReuseComponentCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts index 6cf999a272d61eb0c3610d9d70ff3b55a6551a46..74028790cd61832575de5c4885eea3c67109bc6b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts @@ -14,76 +14,86 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; import { getIdentifierName } from '../utils'; -function isInsideStructAndBuild(node: arkts.AstNode): boolean { - let parentNode = node.parent; - let isInStruct = false; - let isInBuild = false; - while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - if (arkts.isStructDeclaration(parentNode)) { - isInStruct = true; - } - if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { - isInBuild = true; - } - parentNode = parentNode.parent; - } - return isInStruct && isInBuild; -} -function reportNoChildInButtonError(parentNode: arkts.AstNode, context: UISyntaxRuleContext): void { - const siblings = parentNode.getChildren(); - if (!Array.isArray(siblings) || siblings.length < 3) { - return; - } - if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { - context.report({ - node: parentNode, - message: rule.messages.noChildInButton, - fix: (parentNode) => { - const startPosition = arkts.getStartPosition(siblings[2]); - const endPosition = arkts.getEndPosition(siblings[2]); +class NoChildInButtonRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + noChildInButton: `The Button component with a label parameter can not have any child.`, }; - }, - }); - } -} + } -const rule: UISyntaxRule = { - name: 'no-child-in-button', - messages: { - noChildInButton: `The Button component with a label parameter can not have any child.`, - }, - setup(context: UISyntaxRuleContext) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check if the current node is an identifier if (!arkts.isIdentifier(node)) { - return; + return; } const componentName = getIdentifierName(node); // If the current node is 'Button' if (componentName !== 'Button') { - return; + return; + } + if (!this.isInsideStructAndBuild(node)) { + return; } - if (!isInsideStructAndBuild(node)) { - return; + if (!node.parent) { + return; } // Obtain the information of the parent node of the current node let parentNode = node.parent; if (!arkts.isCallExpression(parentNode)) { - return; + return; }; // Gets and traverses all the children of the parent node - reportNoChildInButtonError(parentNode, context); - } - }; - }, + this.reportNoChildInButtonError(parentNode); + } + + private isInsideStructAndBuild(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let parentNode = node.parent; + let isInStruct = false; + let isInBuild = false; + while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + if (arkts.isStructDeclaration(parentNode)) { + isInStruct = true; + } + if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { + isInBuild = true; + } + if (!parentNode.parent) { + return false; + } + parentNode = parentNode.parent; + } + return isInStruct && isInBuild; + } + + private reportNoChildInButtonError(parentNode: arkts.AstNode): void { + const siblings = parentNode.getChildren(); + if (!Array.isArray(siblings) || siblings.length < 3) { + return; + } + if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { + this.report({ + node: parentNode, + message: this.messages.noChildInButton, + fix: () => { + const startPosition = siblings[2].startPosition; + const endPosition = siblings[2].endPosition; + return { + title: 'Remove child components', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + }; -export default rule; \ No newline at end of file +export default NoChildInButtonRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts deleted file mode 100644 index ff42b2180727514624a4dfc03e5d2b05558c27b5..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 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 { getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Decorators that cannot be repeated -const validDecorators = [ - PresetDecorators.ENTRY, - PresetDecorators.COMPONENT_V1, - PresetDecorators.COMPONENT_V2, - PresetDecorators.REUSABLE_V1, - PresetDecorators.PREVIEW, - PresetDecorators.REUSABLE_V2, - PresetDecorators.CUSTOM_DIALOG, -]; - -function checkForDuplicateDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Initialize a map to record decorators and their occurrences - const decoratorCounts: Map = new Map(); - if (!node.definition || !node.definition.annotations) { - return; - } - // Record all decorators and their counts - node.definition.annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - if (!validDecorators.includes(decoratorName)) { - return; - } - - if (decoratorCounts.has(decoratorName)) { - const decoratorInfo = decoratorCounts.get(decoratorName)!; - decoratorInfo.count += 1; - decoratorInfo.annotations.push(annotation); - } else { - decoratorCounts.set(decoratorName, { count: 1, annotations: [annotation] }); - } - }); - - // Process decorators with more than one occurrence - decoratorCounts.forEach(({ count, annotations }, decoratorName) => { - if (count <= 1) { - return; - } - // Report errors for all occurrences except the last one - for (let i = 0; i < annotations.length - 1; i++) { - const prevAnnotation = annotations[i]; - reportDuplicateDecorator(context, prevAnnotation); - } - // For the last occurrence, report an error but do not provide a fix - const lastAnnotation = annotations[annotations.length - 1]; - context.report({ - node: lastAnnotation, - message: rule.messages.duplicateDecorator, - }); - }); -} - -function reportDuplicateDecorator(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage): void { - context.report({ - node: annotation, - message: rule.messages.duplicateDecorator, - fix: () => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} - -const rule: UISyntaxRule = { - name: 'no-duplicate-decorators', - messages: { - duplicateDecorator: `Duplicate decorators for struct are not allowed.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - checkForDuplicateDecorators(context, node); - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts index 60c810180a2b7982a950901930e78174479b28dd..51043718df9ce2cc9fd2a877cdcf4484faec4057 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts @@ -14,59 +14,74 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationUsage, MAX_ENTRY_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const MAX_ENTRY_DECORATOR_COUNT = 1; -function checkDuplicateEntry(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - let entryDecoratorUsages: arkts.AnnotationUsage[] = []; - node.getChildren().forEach((child) => { - if (!arkts.isStructDeclaration(child)) { - return; - } - const entryDecoratorUsage = getAnnotationUsage( - child, - PresetDecorators.ENTRY, - ); - if (entryDecoratorUsage) { - entryDecoratorUsages.push(entryDecoratorUsage); - } - }); - // If more than one entry decorator is recorded, an error is reported - if (entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { - return; - } - entryDecoratorUsages.forEach((entryDocoratorUsage) => { - context.report({ - node: entryDocoratorUsage, - message: rule.messages.duplicateEntry, - fix: (entryDocoratorUsage) => { - const startPosition = arkts.getStartPosition(entryDocoratorUsage); - const endPosition = arkts.getEndPosition(entryDocoratorUsage); +class NoDuplicateEntryRule extends AbstractUISyntaxRule { + private entryDecoratorUsages: arkts.AnnotationUsage[] = []; + private entryDecoratorUsageIndex = 1; + + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + duplicateEntry: `A page can't contain more then one '@Entry' annotation.`, }; - } - }); - }); -} + } -const rule: UISyntaxRule = { - name: 'no-duplicate-entry', - messages: { - duplicateEntry: `An ArkTS file can contain only one '@Entry' decorator.`, - }, - setup(context) { - return { - parsed: (node): void => { - checkDuplicateEntry(node, context); - }, - }; - }, -}; + public beforeTransform(): void { + this.entryDecoratorUsages = []; + this.entryDecoratorUsageIndex = 1; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + let entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); + if (entryDecoratorUsage) { + this.entryDecoratorUsages.push(entryDecoratorUsage); + } + // If more than one entry decorator is recorded, an error is reported + if (this.entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { + return; + } + if (this.entryDecoratorUsageIndex === MAX_ENTRY_DECORATOR_COUNT) { + const entryDecoratorUsage = this.entryDecoratorUsages.at(0)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Entry\' annotation', + range: [startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + } + entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Entry\' annotation', + range: [startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + this.entryDecoratorUsageIndex++; + } +} -export default rule; \ No newline at end of file +export default NoDuplicateEntryRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts new file mode 100644 index 0000000000000000000000000000000000000000..32f2b369d6b65d7ba729d0a9b9cfdc9d5945d6df --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getCurrentFilePath } from '../utils'; + +interface IdInfo { + value: string; + node: arkts.AstNode; +} + +const ID_NAME: string = 'id'; + +class NoDuplicateIdRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + duplicateId: `The current component id "{{id}}" is duplicate with {{path}}:{{line}}:{{index}}.`, + }; + } + + public parsed(node: arkts.AstNode): void { + const usedIds = new Map(); + if (arkts.isBlockStatement(node)) { + this.findAndValidateIds(node, usedIds); + } + } + + private findAndValidateIds( + node: arkts.BlockStatement, + usedIds: Map + ): void { + node.statements.forEach((statement) => { + if ( + arkts.isExpressionStatement(statement) && + arkts.isCallExpression(statement.expression) + ) { + this.findDuplicateComponentIds(statement.expression, usedIds); + } + }); + } + + private findDuplicateComponentIds(node: arkts.CallExpression, usedIds: Map): void { + const idInfo = this.getIdInfo(node); + if (!idInfo) { + return; + } + if (usedIds.has(idInfo.value)) { + this.report({ + node: idInfo.node, + message: this.messages.duplicateId, + data: { + id: idInfo.value, + path: getCurrentFilePath(node) ?? '', + line: idInfo.node.startPosition.line().toString(), + index: idInfo.node.startPosition.index().toString() + } + }); + } else { + // Otherwise, record it + usedIds.set(idInfo.value, idInfo); + } + } + + private getIdInfo(node: arkts.CallExpression): IdInfo | undefined { + const callee = node.expression; + + if (!arkts.isMemberExpression(callee)) { + return undefined; + } + const property = callee.property; + if (!arkts.isIdentifier(property) || property.name !== ID_NAME) { + return undefined; + } + + const strArg = node.arguments.find(arkts.isStringLiteral); + const value = strArg?.str; + if (!value) { + return undefined; + } + return { value, node: property }; + } +} + +export default NoDuplicateIdRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts index fe57d8f11fb33bf5626493a63b1987660b3ed2f0..35d1b31e534cf0edd929a64fa20d69e4216513a6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -14,60 +14,68 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationUsage, MAX_PREVIEW_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const MAX_PREVIEW_DECORATOR_COUNT = 10; +class NoDuplicatePreviewRule extends AbstractUISyntaxRule { + private previewDecoratorUsages: arkts.AnnotationUsage[] = []; + private previewDecoratorUsageIndex = 10; -function checkDuplicatePreview(node: arkts.AstNode, context: UISyntaxRuleContext): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - let previewDecoratorUsages: arkts.AnnotationUsage[] = []; - node.getChildren().forEach((child) => { - if (!arkts.isStructDeclaration(child)) { - return; - } - const previewDecoratorUsage = getAnnotationUsage( - child, - PresetDecorators.PREVIEW, - ); - if (previewDecoratorUsage) { - previewDecoratorUsages.push(previewDecoratorUsage); - } - }); - // If the number of preview decorators is greater than 10, an error is reported - if (previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { - return; - } - previewDecoratorUsages.forEach((previewDecoratorUsage) => { - context.report({ - node: previewDecoratorUsage, - message: rule.messages.duplicateEntry, - fix: (previewDecoratorUsage) => { - const startPosition = arkts.getStartPosition(previewDecoratorUsage); - const endPosition = arkts.getEndPosition(previewDecoratorUsage); + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + duplicateEntry: `A page can contain at most 10 '@Preview' annotations.`, }; - } - }); - }); -} + } + + public beforeTransform(): void { + this.previewDecoratorUsages = []; + this.previewDecoratorUsageIndex = 10; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const previewDecoratorUsage = getAnnotationUsage( + node, + PresetDecorators.PREVIEW, + ); + if (previewDecoratorUsage) { + this.previewDecoratorUsages.push(previewDecoratorUsage); + } + // If the number of preview decorators is less than 10, no error is reported + if (this.previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { + return; + } + if (this.previewDecoratorUsageIndex === MAX_PREVIEW_DECORATOR_COUNT) { + this.previewDecoratorUsages.forEach((previewDecoratorUsage) => { + this.reportError(previewDecoratorUsage); + }); + } else { + let previewDecoratorUsage = this.previewDecoratorUsages.at(this.previewDecoratorUsageIndex); + if (!previewDecoratorUsage) { + return; + } + this.reportError(previewDecoratorUsage); + } + this.previewDecoratorUsageIndex++; + } -const rule: UISyntaxRule = { - name: 'no-duplicate-preview', - messages: { - duplicateEntry: `An ArkTS file con contain at most 10 '@Preview' decorators.`, - }, - setup(context) { - return { - parsed: (node): void => { - checkDuplicatePreview(node, context); - }, - }; - }, -}; + private reportError(errorNode: arkts.AnnotationUsage): void { + let startPosition = errorNode.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + this.report({ + node: errorNode, + message: this.messages.duplicateEntry, + fix: () => { + return { + title: 'Remove the duplicate \'Preview\' annotation', + range: [startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } +} -export default rule; \ No newline at end of file +export default NoDuplicatePreviewRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts deleted file mode 100644 index df1468fd56b754c15210ad0f0e65a9049d625fe5..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 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 { getClassPropertyAnnotationNames } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -export const stateManagementDecorator = { - STATE: 'State', - PROP: 'Prop', - LINK: 'Link', - PROVIDE: 'Provide', - CONSUME: 'Consume' -}; - -const CLASS_PROPERTY_ANNOTATION_ONE: number = 1; -function duplicateState( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(body => { - if (arkts.isClassProperty(body)) { - // Get the list of decorators - const propertyDecorators = getClassPropertyAnnotationNames(body); - // Filter the decorators to get those related to state management - const stateDecorators = propertyDecorators.filter(decorator => - Object.values(stateManagementDecorator).includes(decorator) - ); - const propertyNameNode = body.key; - const attributeName = body.key?.dumpSrc(); - if (!propertyNameNode || !attributeName) { - return; - } - if (stateDecorators.length > CLASS_PROPERTY_ANNOTATION_ONE) { - context.report({ - node: propertyNameNode, - message: rule.messages.duplicateState, - data: { attributeName }, - }); - } - } - }); -} - -const rule: UISyntaxRule = { - name: 'no-duplicate-state-manager', - messages: { - duplicateState: `This attribute '{{attributeName}}' cannot have mutilate state management decorators. `, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - duplicateState(node, context); - }, - }; - }, -}; - -export default rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts index 8815feff46576bfaec7dc8c0713ea58a62144993..7c90f0c9da8d1141a918a76f32d1c67f5f103927 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts @@ -15,61 +15,74 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkNoPropLinkOrObjectLinkInEntry(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Check if the struct has the @Entry decorator - const isEntryComponent = !!getAnnotationUsage(node, PresetDecorators.ENTRY); - if (!isEntryComponent) { - return; - } - node.definition.body.forEach(body => { - if (!arkts.isClassProperty(body)) { - return; - } - const invalidDecorators = [PresetDecorators.PROP, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK]; - // Check if any invalid decorators are applied to the class property - body.annotations?.forEach(annotation => { - reportInvalidDecorator(context, annotation, invalidDecorators); - }); - }); -} +const invalidDecorators = [PresetDecorators.PROP_REF, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK]; -function reportInvalidDecorator(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, - invalidDecorators: string[],): void { - if (annotation.expr && invalidDecorators.includes(annotation.expr.dumpSrc())) { - const decorator = annotation.expr.dumpSrc(); - context.report({ - node: annotation, - message: rule.messages.invalidUsage, - data: { decorator }, - fix: (annotation) => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); +class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + disallowDecoratorInEntry: `The '@Entry' component '{{componentName}}' cannot have the '@{{decoratorName}}' property '{{propertyName}}'.`, }; - }, - }); - } -} - -const rule: UISyntaxRule = { - name: 'no-prop-link-objectlink-in-entry', - messages: { - invalidUsage: `@{{decorator}} decorator cannot be used for '@Entry' decorated components.`, - }, - setup(context) { - return { - parsed: (node): void => { + } + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + this.checkNoPropLinkOrObjectLinkInEntry(node); + } + + private checkNoPropLinkOrObjectLinkInEntry(node: arkts.StructDeclaration): void { + // Check if the struct has the @Entry decorator + const isEntryComponent = !!getAnnotationUsage(node, PresetDecorators.ENTRY); + if (!node.definition.ident || !arkts.isIdentifier(node.definition.ident)) { + return; + } + const componentName = node.definition.ident.name; + if (!isEntryComponent) { + return; } - checkNoPropLinkOrObjectLinkInEntry(context, node); - }, - }; - }, -}; + node.definition.body.forEach(body => { + if (!arkts.isClassProperty(body)) { + return; + } + if (!body.key || !arkts.isIdentifier(body.key)) { + return; + } + const propertyName = body.key.name; + // Check if any invalid decorators are applied to the class property + body.annotations?.forEach(annotation => { + this.reportInvalidDecorator(annotation, invalidDecorators, componentName, propertyName); + }); + }); + } + + private reportInvalidDecorator(annotation: arkts.AnnotationUsage, + invalidDecorators: string[], componentName: string, propertyName: string): void { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + invalidDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.report({ + node: annotation, + message: this.messages.disallowDecoratorInEntry, + data: { + componentName, + decoratorName, + propertyName, + }, + fix: (annotation) => { + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } +} -export default rule; +export default NoPropLinkObjectLinkInEntryRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts index e61af0b5682a58405b0de144d1331314fd665d4d..5940191f68b5f240a1b74058874dd0a235291b64 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts @@ -14,34 +14,38 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { isBuiltInAttribute } from '../utils'; -const rule: UISyntaxRule = { - name: 'no-same-as-built-in-attribute', - messages: { - duplicateName: `The struct name '{{structName}}' should not have the same name as a built-in attribute.`, - }, - setup(context) { - const builtInAttributes = ['fontColor', 'width', 'height', 'size', 'border', 'backgroundColor', 'margin', - 'padding']; +class NoSameAsBuiltInAttributeRule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - const structName = node.definition.ident?.name ?? ' '; - const structIdent = node.definition.ident; - // If the struct name matches any built-in attribute, report an error - if (builtInAttributes.includes(structName) && structIdent) { - context.report({ - node: structIdent, - message: rule.messages.duplicateName, - data: { structName } - }); - } - }, + duplicateName: `The struct '{{structName}}' cannot have the same name as the built-in attribute '{{builtInName}}'.`, }; - }, + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; + } + const structIdent = node.definition.ident; + const structName = node.definition.ident?.name ?? ' '; + // If the struct name matches any built-in attribute, report an error + if (structIdent && isBuiltInAttribute(this.context, structName)) { + const builtInName = structName; + this.report({ + node: structIdent, + message: this.messages.duplicateName, + data: { structName, builtInName } + }); + } + } }; -export default rule; +export default NoSameAsBuiltInAttributeRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts index 95b605f2cfdae926f518c06a859a21cd57c1f4c5..b18d51a2ff39f3f4fb84c2ebfb513bd40d4fd205 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts @@ -14,93 +14,113 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getIdentifierName, PresetDecorators } from '../utils/index'; -function getObservedDecorator(node: arkts.AstNode, observedClasses: string[], observedV2Classes: string[]): void { - for (const child of node.getChildren()) { - // Check if it is of the ClassDeclaration type - if (arkts.isClassDeclaration(child)) { - // Get a list of annotations - const annotations = child.definition?.annotations ?? []; - // Check for @Observed decorators - const hasObservedDecorator = annotations.find((annotation: any) => - annotation.expr.name === PresetDecorators.OBSERVED_V1); - // Check if there is a @ObservedV2 decorator - const hasObservedV2Decorator = annotations.find((annotation: any) => - annotation.expr.name === PresetDecorators.OBSERVED_V2); - // If there is a @Observed decorator, record the class name - if (hasObservedDecorator) { - const className = child.definition?.ident?.name ?? ''; - observedClasses.push(className); - } - // If there is a @ObservedV2 decorator, record the class name - if (hasObservedV2Decorator) { - const className = child.definition?.ident?.name ?? ''; - observedV2Classes.push(className); - } - } - } -} +class ObservedHeritageCompatibleCheckRule extends AbstractUISyntaxRule { + // Record the class name decorated with @Observed and @ObservedV2 + private observedClasses: string[] = []; + private observedV2Classes: string[] = []; -function checkInheritanceCompatibility(context: UISyntaxRuleContext, node: arkts.ClassDeclaration, - observedClasses: string[], observedV2Classes: string[]): void { - const observedV1Decorator = node.definition?.annotations?.find(annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V1 - ); - const observedV2Decorator = node.definition?.annotations?.find(annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V2 - ); - - //Get the name of the superClass - const superClassName = node.definition?.super?.dumpSrc(); - if (!superClassName) { - return; + public setup(): Record { + return { + incompatibleHeritageObservedToObservedV2: `A class decorated by '@Observed' cannot inherit from a class decorated by '@ObservedV2'.`, + incompatibleHeritageObservedV2ToObserved: `A class decorated by '@ObservedV2' cannot inherit from a class decorated by '@Observed'.`, + }; } - // Verify that the inheritance relationship is compatible - if (observedV1Decorator && observedV2Classes.includes(superClassName)) { - context.report({ - node: observedV1Decorator, - message: rule.messages.incompatibleHeritageObservedToObservedV2, - }); + + public beforeTransform(): void { + this.observedClasses = []; + this.observedV2Classes = []; } - if (observedV2Decorator && observedClasses.includes(superClassName)) { - context.report({ - node: observedV2Decorator, - message: rule.messages.incompatibleHeritageObservedV2ToObserved, - }); + + public parsed(node: arkts.AstNode): void { + // Check if it's of type "Program". + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + this.getObservedDecorator(node, this.observedClasses, this.observedV2Classes); + } + // Check if the current node is a class declaration + if (!arkts.isClassDeclaration(node)) { + return; + } + this.checkInheritanceCompatibility(node, this.observedClasses, this.observedV2Classes); } -} -const rule: UISyntaxRule = { - name: 'observed-heritage-compatible-check', - messages: { - incompatibleHeritageObservedToObservedV2: `The current class is decorated by '@Observed', it cannot inherit a class decorated by '@ObservedV2'.`, - incompatibleHeritageObservedV2ToObserved: `The current class is decorated by '@ObservedV2', it cannot inherit a class decorated by '@Observed'.`, - }, - setup(context) { - // Record the class name decorated with @Observed and @ObservedV2 - const observedClasses: string[] = []; - const observedV2Classes: string[] = []; - return { - parsed: (node): void => { - // Check if it's of type "Program". - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - getObservedDecorator(node, observedClasses, observedV2Classes); + private getObservedDecorator(node: arkts.AstNode, observedClasses: string[], observedV2Classes: string[]): void { + for (const child of node.getChildren()) { + // Check if it is of the ClassDeclaration type + if (arkts.isClassDeclaration(child)) { + // Get a list of annotations + const annotations = child.definition?.annotations ?? []; + // Check for @Observed decorators + const hasObservedDecorator = annotations.find((annotation: any) => + annotation.expr.name === PresetDecorators.OBSERVED_V1); + // Check if there is a @ObservedV2 decorator + const hasObservedV2Decorator = annotations.find((annotation: any) => + annotation.expr.name === PresetDecorators.OBSERVED_V2); + // If there is a @Observed decorator, record the class name + if (hasObservedDecorator) { + const className = child.definition?.ident?.name ?? ''; + observedClasses.push(className); } - - // Check if the current node is a class declaration - if (!arkts.isClassDeclaration(node)) { - return; + // If there is a @ObservedV2 decorator, record the class name + if (hasObservedV2Decorator) { + const className = child.definition?.ident?.name ?? ''; + observedV2Classes.push(className); } - checkInheritanceCompatibility(context, node, observedClasses, observedV2Classes); - }, - }; - }, + } + } + } + + private checkInheritanceCompatibility( + node: arkts.ClassDeclaration, + observedClasses: string[], + observedV2Classes: string[]): void { + const observedV1Decorator = node.definition?.annotations?.find(annotations => + annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V1 + ); + const observedV2Decorator = node.definition?.annotations?.find(annotations => + annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V2 + ); + + //Get the name of the superClass + if (!node.definition?.super) { + return; + } + if (arkts.isETSTypeReference(node.definition?.super)) { + if (!node.definition.super.part) { + return; + } + if (!arkts.isETSTypeReferencePart(node.definition?.super.part)) { + return; + } + if (!node.definition?.super.part.name) { + return; + } + const superClassName = getIdentifierName(node.definition?.super.part.name); + + if (!superClassName) { + return; + } + // Verify that the inheritance relationship is compatible + if (observedV1Decorator && observedV2Classes.includes(superClassName)) { + this.report({ + node: observedV1Decorator, + message: this.messages.incompatibleHeritageObservedToObservedV2, + }); + } + if (observedV2Decorator && observedClasses.includes(superClassName)) { + this.report({ + node: observedV2Decorator, + message: this.messages.incompatibleHeritageObservedV2ToObserved, + }); + } + } + } }; -export default rule; \ No newline at end of file +export default ObservedHeritageCompatibleCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts index e02304b3ceb27c0a1b36ee159889a7f672c27144..79d42a4d670b9a99bb6626fd74100fa3b9ab64ad 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts @@ -15,33 +15,33 @@ import * as arkts from '@koalaui/libarkts'; import { PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'observed-observedV2-check', - messages: { - conflictingDecorators: `A class cannot be decorated by both '@Observed' and '@ObservedV2' at the same time.`, - }, - setup(context) { +class ObservedObservedV2Rule extends AbstractUISyntaxRule { + public setup(): Record { return { - parsed: (node): void => { - if (!arkts.isClassDeclaration(node)) { - return; - } - const hasObservedDecorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V1); - const hasObservedV2Decorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V2); - // If the current class is decorated by @Observed and @ObservedV2, an error is reported - if (hasObservedDecorator && hasObservedV2Decorator) { - context.report({ - node: hasObservedDecorator, - message: rule.messages.conflictingDecorators, - }); - } - }, + conflictingDecorators: `A class cannot be decorated by both '@Observed' and '@ObservedV2' at the same time.`, }; - }, + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node)) { + return; + } + const hasObservedDecorator = node.definition?.annotations?.find(annotations => annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V1); + const hasObservedV2Decorator = node.definition?.annotations?.find(annotations => annotations.expr && + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V2); + // If the current class is decorated by @Observed and @ObservedV2, an error is reported + if (hasObservedDecorator && hasObservedV2Decorator) { + this.report({ + node: hasObservedDecorator, + message: this.messages.conflictingDecorators, + }); + } + } }; -export default rule; +export default ObservedObservedV2Rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts index 278a9bc7326969f423e16e8d080bb99fe4ab6b15..fce5a72c87ace28a055f5e63ab268a9d4603f9eb 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts @@ -14,141 +14,217 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { + PresetDecorators, getAnnotationUsage, findDecorator, getClassDeclarationAnnotation +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'observedV2-trace-usage-validation', - messages: { - observedV2DecoratorError: `The '@ObservedV2' decorator can only be used in 'class'.`, - traceDecoratorError: `The '@Trace' decorator can only be used in 'class'.`, - traceInObservedV2Error: `The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2'.`, - traceMemberVariableError: `The '@Trace' decorator can only decorate member variables within a 'class' decorated with '@ObservedV2'.`, - }, - setup(context) { - return { - parsed: (node): void => { - validateTraceDecoratorUsage(node, context); - }, - }; - }, -}; +class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + observedV2DecoratorError: `The '@ObservedV2' annotation can only be used in 'class'.`, + traceDecoratorError: `The '@Trace' annotation can only be used in 'class'.`, + traceMemberVariableError: `The '@Trace' annotation can only decorate member variables within a 'class' decorated with '@ObservedV2'.`, + //The repair logic is different, if there is v1, update to v2 + traceMustUsedWithObservedV2: `The '@Trace' annotation can only be used within a 'class' decorated with 'ObservedV2'.`, + traceMustUsedWithObservedV2Update: `The '@Trace' annotation can only be used within a 'class' decorated with 'ObservedV2'.`, + }; + } -function reportObservedV2DecoratorError(context: UISyntaxRuleContext, hasObservedV2Decorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasObservedV2Decorator, - message: rule.messages.observedV2DecoratorError, - fix: (hasObservedV2Decorator) => { - const startPosition = arkts.getStartPosition(hasObservedV2Decorator); - const endPosition = arkts.getEndPosition(hasObservedV2Decorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + public parsed(node: arkts.AstNode): void { + this.validateTraceDecoratorUsage(node); + } -function reportTraceMemberVariableError(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceMemberVariableError, - fix: (hasTraceDecorator) => { - const startPosition = arkts.getStartPosition(hasTraceDecorator); - const endPosition = arkts.getEndPosition(hasTraceDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private getObservedDecorator(node: arkts.ClassDeclaration): arkts.AnnotationUsage | undefined { + if (!node.definition) { + return undefined; + } + return getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V1); + } -function tracePerportyRule( - context: UISyntaxRuleContext, - currentNode: arkts.AstNode, - hasTraceDecorator: arkts.AnnotationUsage -): void { - if (arkts.isStructDeclaration(currentNode)) { - reportTraceDecoratorError(context, hasTraceDecorator); - } else if (arkts.isClassDeclaration(currentNode)) { - // The '@Trace' decorator can only be used in a 'class' decorated with '@ObservedV2' - if ( - !currentNode.definition?.annotations?.some((annotation: arkts.AnnotationUsage) => { - return ( - !!annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.OBSERVED_V2 - ); - }) - ) { - reportTraceInObservedV2Error(context, hasTraceDecorator, currentNode); + private reportObservedV2DecoratorError(observedV2Decorator: arkts.AnnotationUsage) + : void { + this.report({ + node: observedV2Decorator, + message: this.messages.observedV2DecoratorError, + fix: (observedV2Decorator) => { + let startPosition = observedV2Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = observedV2Decorator.endPosition; + return { + title: 'Remove the @ObservedV2 annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private reportTraceMemberVariableError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceMemberVariableError, + fix: (traceDecorator) => { + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; + return { + title: 'Remove the @Trace annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private tracePropertyRule( + currentNode: arkts.AstNode, + traceDecorator: arkts.AnnotationUsage): void { + if (arkts.isStructDeclaration(currentNode)) { + this.reportTraceDecoratorError(traceDecorator); + } else if (arkts.isClassDeclaration(currentNode) && currentNode.definition) { + const observedDecorator = this.getObservedDecorator(currentNode); + const observedV2 = currentNode.definition.annotations.some(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.OBSERVED_V2 + ); + if (!observedV2 && !observedDecorator) { + this.reportTraceMustUsedWithObservedV2(traceDecorator, currentNode); + } else if (!observedV2 && observedDecorator) { + this.reportTraceMustUsedWithObservedV2Update(traceDecorator, observedDecorator); + } } } -} -function reportTraceDecoratorError(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage) - : void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceDecoratorError, - fix: (hasTraceDecorator) => { - const startPosition = arkts.getStartPosition(hasTraceDecorator); - const endPosition = arkts.getEndPosition(hasTraceDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private reportTraceDecoratorError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceDecoratorError, + fix: (traceDecorator) => { + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; + return { + title: 'Remove the @Trace annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -function reportTraceInObservedV2Error(context: UISyntaxRuleContext, hasTraceDecorator: arkts.AnnotationUsage, - currentNode: arkts.ClassDeclaration): void { - context.report({ - node: hasTraceDecorator, - message: rule.messages.traceInObservedV2Error, - fix: () => { - const startPosition = arkts.getStartPosition(currentNode); - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.OBSERVED_V2}\n`, - }; - }, - }); -} + private reportTraceMustUsedWithObservedV2(traceDecorator: arkts.AnnotationUsage, + currentNode: arkts.ClassDeclaration): void { + this.report({ + node: traceDecorator, + message: this.messages.traceMustUsedWithObservedV2, + fix: () => { + const startPosition = currentNode.startPosition; + return { + title: 'Add @ObservedV2 annotation', + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } -function validateTraceDecoratorUsage(node: arkts.AstNode, context: UISyntaxRuleContext): void { - let currentNode = node; - if (arkts.isStructDeclaration(node)) { - // Check whether the current custom component is decorated by the @ObservedV2 decorator - const hasObservedV2Decorator = getAnnotationUsage(node, PresetDecorators.OBSERVED_V2); - if (hasObservedV2Decorator) { - reportObservedV2DecoratorError(context, hasObservedV2Decorator); + private reportTraceMustUsedWithObservedV2Update(traceDecorator: arkts.AnnotationUsage, + observedDecorator: arkts.AnnotationUsage): void { + this.report({ + node: traceDecorator, + message: this.messages.traceMustUsedWithObservedV2Update, + fix: () => { + const startPosition = observedDecorator.startPosition; + const endPosition = observedDecorator.endPosition; + return { + title: 'Change @Observed to @ObservedV2', + range: [startPosition, endPosition], + code: `${PresetDecorators.OBSERVED_V2}`, + }; + }, + }); } - } - if (arkts.isClassProperty(node)) { - const hasTraceDecorator = node.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.TRACE); - if (hasTraceDecorator) { - // Iterate up the parent node to check whether it is a class or a custom component - while (!arkts.isStructDeclaration(currentNode) && !arkts.isClassDeclaration(currentNode)) { - currentNode = currentNode.parent; - } - // The '@Trace' decorator can only be used in 'class' - tracePerportyRule(context, currentNode, hasTraceDecorator); + + private isInClassDeclaration(node: arkts.AstNode): boolean { + while (!arkts.isClassDeclaration(node)) { + if (!node.parent) { + return false; + } + node = node.parent; + } + return true; + } + + private checkAndReportObservedV2Decorator( + node: arkts.FunctionDeclaration | arkts.VariableDeclaration | arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration | arkts.ClassProperty): void { + const observedV2Decorator = findDecorator(node, PresetDecorators.OBSERVED_V2); + if (observedV2Decorator) { + this.reportObservedV2DecoratorError(observedV2Decorator); + } + } + + private validateTraceDecoratorUsage(node: arkts.AstNode): void { + let currentNode = node; + if (arkts.isStructDeclaration(node)) { + // Check whether the current custom component is decorated by the @ObservedV2 decorator + const observedV2Decorator = getAnnotationUsage(node, PresetDecorators.OBSERVED_V2); + const traceDecorator = getAnnotationUsage(node, PresetDecorators.TRACE); + if (observedV2Decorator) { + this.reportObservedV2DecoratorError(observedV2Decorator); + } + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } else if ( + arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.checkAndReportObservedV2Decorator(node); + const traceDecorator = findDecorator(node, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } else if (arkts.isClassProperty(node)) { + this.checkTraceDecoratorUsageInClassProperty(node, currentNode); + this.checkAndReportObservedV2Decorator(node); + } + if (arkts.isMethodDefinition(node) && this.isInClassDeclaration(currentNode)) { + // Check that @Trace is in the correct location + const traceDecorator = findDecorator(node.scriptFunction, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceMemberVariableError(traceDecorator); + } + } else if (arkts.isMethodDefinition(node) && !this.isInClassDeclaration(currentNode)) { + const traceDecorator = findDecorator(node.scriptFunction, PresetDecorators.TRACE); + if (traceDecorator) { + this.reportTraceDecoratorError(traceDecorator); + } + } } - } - if (arkts.isMethodDefinition(node)) { - // Check that @Trace is in the correct location - const hasTraceDecorator = node.scriptFunction.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.TRACE); - if (hasTraceDecorator) { - reportTraceMemberVariableError(context, hasTraceDecorator); + + private checkTraceDecoratorUsageInClassProperty( + node: arkts.ClassProperty, + currentNode: arkts.AstNode,): void { + const traceDecorator = findDecorator(node, PresetDecorators.TRACE); + if (traceDecorator) { + // Iterate up the parent node to check whether it is a class or a custom component + while (!arkts.isStructDeclaration(currentNode) && !arkts.isClassDeclaration(currentNode)) { + if (!currentNode.parent) { + return; + } + currentNode = currentNode.parent; + } + // The '@Trace' decorator can only be used in 'class' + this.tracePropertyRule(currentNode, traceDecorator); + } } - } -} +}; -export default rule; +export default ObservedV2TraceUsageValidationRule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts index 356a28098854e9031f63161d2cf9c69aa0b39f3d..ce916bb8318db96da0568e5f5d7c076c5c73389a 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts @@ -14,98 +14,123 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { PresetDecorators, getAnnotationName, getAnnotationUsage, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function findPropertyDecorator( - node: arkts.ClassProperty, - decoratorName: string -): arkts.AnnotationUsage | undefined { - const annotation = node.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decoratorName - ); - return annotation; -} +class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { + private static readonly oldV1Decorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.WATCH, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.OBJECT_LINK, + ]; -function paramDecoratorError( - context: UISyntaxRuleContext, - hasParamDecorator: arkts.AnnotationUsage, - hasComponentDecorator: arkts.AnnotationUsage -): void { - context.report({ - node: hasParamDecorator, - message: rule.messages.paramDecoratorError, - fix: () => { - const startPosition = arkts.getStartPosition(hasComponentDecorator); - const endPosition = arkts.getEndPosition(hasComponentDecorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}`, - }; - }, - }); -} + private static readonly newV2decorators: string[] = [ + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.ONCE, + PresetDecorators.EVENT, + PresetDecorators.MONITOR, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.COMPUTED, + ]; -function stateDecoratorError( - context: UISyntaxRuleContext, - hasStateDecorator: arkts.AnnotationUsage, - hasComponentV2Decorator: arkts.AnnotationUsage -): void { - context.report({ - node: hasStateDecorator, - message: rule.messages.stateDecoratorError, - fix: () => { - const startPosition = arkts.getStartPosition(hasComponentV2Decorator); - const endPosition = arkts.getEndPosition(hasComponentV2Decorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V1}`, - }; - }, - }); -} + public setup(): Record { + return { + oldAndNewDecoratorsMixUse: `The '@{{decoratorName}}' annotation can only be used in a 'struct' decorated with '@{{component}}'.`, + }; + } + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + // Gets the decorator version of a custom component + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + node.definition.body.forEach((property) => { + if (!arkts.isClassProperty(property)) { + return; + } + const newDecorator = this.findPropertyDecorator(property, OldNewDecoratorMixUseCheckRule.newV2decorators); + const oldDecorator = this.findPropertyDecorator(property, OldNewDecoratorMixUseCheckRule.oldV1Decorators); + // Check that the new decorator is used for component v2 + if (newDecorator && !componentV2Decorator && componentDecorator) { + this.reportErrorAndChangeDecorator(newDecorator, componentDecorator, PresetDecorators.COMPONENT_V2); + } + if (newDecorator && !componentDecorator && !componentV2Decorator) { + this.reportErrorAndAddDecorator(node, newDecorator); + } + // Check that the old decorator is used for component v1 + if (oldDecorator && !componentDecorator && componentV2Decorator) { + this.reportErrorAndChangeDecorator(oldDecorator, componentV2Decorator, PresetDecorators.COMPONENT_V1); + } + }); + } -function findDecoratorError( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext -): void { - const hasComponentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const hasComponentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - // Check where @Param and @State are used - node.definition.body.forEach((property) => { - if (arkts.isClassProperty(property)) { - const hasParamDecorator = findPropertyDecorator(property, PresetDecorators.PARAM); - const hasStateDecorator = findPropertyDecorator(property, PresetDecorators.STATE); - // Check that @Param is in the correct location - if (hasParamDecorator && !hasComponentV2Decorator && hasComponentDecorator) { - paramDecoratorError(context, hasParamDecorator, hasComponentDecorator); - } - // Check that @State is in the correct location - if (hasStateDecorator && !hasComponentDecorator && hasComponentV2Decorator) { - stateDecoratorError(context, hasStateDecorator, hasComponentV2Decorator); - } + private findPropertyDecorator( + node: arkts.ClassProperty, + decoratorList: string[] + ): arkts.AnnotationUsage | undefined { + return node.annotations?.find(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + decoratorList.includes(getIdentifierName(annotation.expr)) + ); } - }); -} + private reportErrorAndChangeDecorator( + errorDecorator: arkts.AnnotationUsage, + hasComponentV2Decorator: arkts.AnnotationUsage, + structDecoratorName: string + ): void { + let propertyDecoratorName = getAnnotationName(errorDecorator); + const curStructDecoratorName = structDecoratorName === + PresetDecorators.COMPONENT_V2 ? PresetDecorators.COMPONENT_V1 : PresetDecorators.COMPONENT_V2; + this.report({ + node: errorDecorator, + message: this.messages.oldAndNewDecoratorsMixUse, + data: { + decoratorName: propertyDecoratorName, + component: structDecoratorName, + }, + fix: () => { + return { + title: `Change @${curStructDecoratorName} to @${structDecoratorName}`, + range: [hasComponentV2Decorator.startPosition, hasComponentV2Decorator.endPosition], + code: structDecoratorName, + }; + }, + }); + } -const rule: UISyntaxRule = { - name: 'old-new-decorator-mix-use-check', - messages: { - paramDecoratorError: `The '@Param' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, - stateDecoratorError: `The '@State' decorator can only be used in a 'struct' decorated with '@Component'.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; - } - findDecoratorError(node, context); - }, - }; - }, -}; + private reportErrorAndAddDecorator( + structNode: arkts.StructDeclaration, + errorDecorator: arkts.AnnotationUsage, + ): void { + let propertyDecoratorName = getAnnotationName(errorDecorator); + this.report({ + node: errorDecorator, + message: this.messages.oldAndNewDecoratorsMixUse, + data: { + decoratorName: propertyDecoratorName, + component: PresetDecorators.COMPONENT_V2, + }, + fix: () => { + return { + title: 'Add @ComponentV2 annotation', + range: [structNode.startPosition, structNode.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); + } +} -export default rule; +export default OldNewDecoratorMixUseCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts index fabe04af00c11adc8fd5e43f1a1e279e1c307bf6..0c0d16f8209d8216c83551262df98a3261a333ad 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -14,174 +14,131 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationUsage, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getClassPropertyAnnotationNames, PresetDecorators, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function findDecorator(member: arkts.ClassProperty, decorator: string): arkts.AnnotationUsage | undefined { - return member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === decorator - ); -} - -// Check that the property decorator complies with the rules -function validatePropertyAnnotations( - body: arkts.ClassProperty, - context: UISyntaxRuleContext, - hasOnceDecorator: arkts.AnnotationUsage | undefined -): void { - const propertyAnnotations = getClassPropertyAnnotationNames(body); - hasOnceDecorator = findDecorator(body, PresetDecorators.ONCE); - if (hasOnceDecorator) { - const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); - // If @Once is found, check if @Param is also used - if (!isParamUsed) { - reportMissingParamWithOnce(hasOnceDecorator, context); - } else { - // If both @Once and @Param are used, check for other - // incompatible decorators - const otherDecorators = body.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() !== PresetDecorators.ONCE && - annotation.expr.dumpSrc() !== PresetDecorators.PARAM - ); - reportInvalidDecoratorsWithOnceAndParam(otherDecorators, context); +class OnceDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidMemberDecorate: `'@Once' can only decorate member property.`, + invalidDecorator: `When a variable decorated with '@Once', it must also be decorated with '@Param'.`, + invalidNOtInStruct: `'@Once' annotation can only be used with 'struct'.` + }; } - } -} -function reportMissingParamWithOnce( - hasOnceDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasOnceDecorator) { - return; - } - context.report({ - node: hasOnceDecorator, - message: rule.messages.invalidDecorator, - fix: (hasOnceDecorator) => { - const startPosition = arkts.getEndPosition(hasOnceDecorator); - const endPosition = arkts.getEndPosition(hasOnceDecorator); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.PARAM}` - }; + public parsed(node: arkts.AstNode): void { + + this.validateOnlyInStruct(node); + this.validateOnlyOnProperty(node); + + if (arkts.isStructDeclaration(node)) { + this.validateDecorator(node); + } } - }); -} -function reportInvalidDecoratorsWithOnceAndParam( - otherDecorators: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!otherDecorators) { - return; - } - context.report({ - node: otherDecorators, - message: rule.messages.invalidDecorator, - fix: (otherDecorators) => { - const startPosition = arkts.getStartPosition(otherDecorators); - const endPosition = arkts.getEndPosition(otherDecorators); - return { - range: [startPosition, endPosition], - code: '' - }; + private validateOnlyInStruct(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateOnceDecoratorUsage(member, this.messages.invalidNOtInStruct); + + } + if (arkts.isMethodDefinition(member)) { + this.validateOnceDecoratorUsage(member.scriptFunction, this.messages.invalidNOtInStruct); + } + }); + return; + } + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateOnceDecoratorUsage(node, this.messages.invalidNOtInStruct); + return; + } } - }); -} -// Check if the method is @Once decorated (not allowed) -function validateMethodAnnotations(body: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const methodAnnotations = body.scriptFunction.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.ONCE - ); - if (methodAnnotations) { - context.report({ - node: methodAnnotations, - message: rule.messages.invalidMemberDecorate, - fix: (methodAnnotations) => { - const startPosition = arkts.getStartPosition(methodAnnotations); - const endPosition = arkts.getEndPosition(methodAnnotations); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); - } -} + private validateOnceDecoratorUsage( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + message: string, + ): void { + const decorator = findDecorator(node, PresetDecorators.ONCE); + if (!decorator) { + return; + } -function invalidComponentUsage( - body: arkts.ClassProperty, - hasOnceDecorator: arkts.AnnotationUsage | undefined, - componentV2DocoratorUsage: arkts.AnnotationUsage | undefined, - componentDocoratorUsage: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - hasOnceDecorator = findDecorator(body, PresetDecorators.ONCE); - if (hasOnceDecorator && !componentV2DocoratorUsage && componentDocoratorUsage) { - context.report({ - node: hasOnceDecorator, - message: rule.messages.invalidUsage, - fix: (hasOnceDecorator) => { - const startPosition = arkts.getStartPosition(componentDocoratorUsage); - const endPosition = arkts.getEndPosition(componentDocoratorUsage); - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}` - }; - } - }); - } -} + this.report({ + node: decorator, + message: message, + fix: (decorator) => { + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = decorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + + private validateOnlyOnProperty(node: arkts.AstNode): void { + if (arkts.isFunctionDeclaration(node) || arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateOnceDecoratorUsage(node, this.messages.invalidMemberDecorate); + } + } -function validateDecorater( - node: arkts.StructDeclaration, - hasOnceDecorator: arkts.AnnotationUsage | undefined, - componentV2DocoratorUsage: arkts.AnnotationUsage | undefined, - componentDocoratorUsage: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext, -): void { - node.definition?.body.forEach(body => { - // Check if @Once is used on a property and if @Param is used with - if (arkts.isClassProperty(body)) { - validatePropertyAnnotations(body, context, hasOnceDecorator); - // If @Once is used but not in a @ComponentV2 struct, report an error - invalidComponentUsage(body, hasOnceDecorator, componentV2DocoratorUsage, componentDocoratorUsage, context); + private validateDecorator( + node: arkts.StructDeclaration, + ): void { + node.definition?.body.forEach(body => { + // Check if @Once is used on a property and if @Param is used with + if (arkts.isClassProperty(body)) { + this.validatePropertyAnnotations(body); + } + }); } - if (!arkts.isMethodDefinition(body)) { - return; + + private validatePropertyAnnotations( + body: arkts.ClassProperty + ): void { + const propertyAnnotations = getClassPropertyAnnotationNames(body); + const onceDecorator = findDecorator(body, PresetDecorators.ONCE); + if (onceDecorator) { + const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); + // If @Once is found, check if @Param is not used + if (!isParamUsed) { + this.reportMissingParamWithOnce(onceDecorator); + } + } } - // Check if @Once is used on a method (which is not allowed) - validateMethodAnnotations(body, context); - }); -} -const rule: UISyntaxRule = { - name: 'once-decorator-check', - messages: { - invalidUsage: `@Once can only decorate member properties in a @ComponentV2 struct.`, - invalidMemberDecorate: `@Once can only decorate member properties.`, - invalidDecorator: `@Once must be only used with @Param. ` - }, - setup(context) { - return { - parsed: (node): void => { - // Check if the node is a struct declaration - if (!arkts.isStructDeclaration(node)) { - return; + private reportMissingParamWithOnce( + onceDecorator: arkts.AnnotationUsage | undefined + ): void { + if (!onceDecorator) { + return; } - let hasOnceDecorator: arkts.AnnotationUsage | undefined; - // Check if the struct is decorated with @ComponentV2 - const componentV2DocoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const componentDocoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - validateDecorater(node, hasOnceDecorator, componentV2DocoratorUsage, componentDocoratorUsage, context); - }, - }; - }, -}; + this.report({ + node: onceDecorator, + message: this.messages.invalidDecorator, + fix: () => { + const startPosition = onceDecorator.endPosition; + const endPosition = onceDecorator.endPosition; + return { + title: 'Add @Param annotation', + range: [startPosition, endPosition], + code: `@${PresetDecorators.PARAM}` + }; + } + }); + } +} -export default rule; +export default OnceDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts index 66dd9cdbd86d4d9028486a94d949d67bfa80c53b..38b35cae0d139936f75a095d48a07d3a6e69cc5a 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts @@ -14,55 +14,110 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getAnnotationName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getAnnotationName, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const allowedDecorators = new Set(['Extend', 'Builder', 'Styles']); +const allowedDecorators = PresetDecorators.BUILDER; +const PARAM_THIS_NAME = '=t'; +const DECORATOR_LIMIT = 1; -function validateFunctionDecorator(node: arkts.EtsScript, context: UISyntaxRuleContext): void { - node.statements.forEach((statement) => { - // If the node is not a function declaration, it is returned - if (!arkts.isFunctionDeclaration(statement)) { - return; +class OneDecoratorOnFunctionMethodRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidDecorator: `A function can only be decorated by the 'Builder'.`, + }; } - const annotations = statement.annotations; - // If there is no annotation, go straight back - if (!annotations) { - return; - } - // Check that each annotation is in the list of allowed decorators - annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - // rule1: misuse of decorator, only '@Extend', '@Builder' , '@Styles' decorators allowed on global functions - if (!allowedDecorators.has(decoratorName)) { - context.report({ - node: annotation, - message: rule.messages.invalidDecorator, - data: { - decoratorName, - }, - }); - } - }); - }); -} -const rule: UISyntaxRule = { - name: 'one-decorator-on-function-method', - messages: { - invalidDecorator: `misuse of '@{{decoratorName}}' decorator, only '@Extend', '@Builder' and '@Styles' decorators allowed on global functions.`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { + public parsed(node: arkts.AstNode): void { // If the node is not an ETS script, it is returned directly if (!arkts.isEtsScript(node)) { - return; + return; + } + this.validateFunctionDecorator(node); + } + + private validateFunctionDecorator(node: arkts.EtsScript): void { + node.statements.forEach((statement) => { + // If the node is not a function declaration, it is returned + if (!arkts.isFunctionDeclaration(statement)) { + return; + } + const annotations = statement.annotations; + // If there is no annotation, go straight back + if (!annotations) { + return; + } + // @AnimatableExtend decorators can only be used with functions with this parameter. + const animatableExtendDecorator = this.findDecorator(annotations, PresetDecorators.ANIMATABLE_EXTEND); + if (arkts.isScriptFunction(statement.scriptFunction) && animatableExtendDecorator) { + const member = statement.scriptFunction; + if (this.hasThisParameter(member)) { + return; + } + } + // Check that each annotation is in the list of allowed decorators + this.validateAllowedDecorators(annotations, this.otherDecoratorFilter(annotations)); + }); + } + + private findDecorator(annotations: arkts.AnnotationUsage[], decorator: string): arkts.AnnotationUsage | undefined { + return annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decorator + ); + } + + private otherDecoratorFilter(annotations: arkts.AnnotationUsage[]): arkts.AnnotationUsage | undefined { + return annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.BUILDER + ); + } + + private hasThisParameter(member: arkts.ScriptFunction): boolean { + return member.params.some((param) => { + return arkts.isEtsParameterExpression(param) && + arkts.isIdentifier(param.identifier) && + param.identifier.name === PARAM_THIS_NAME; + }); + } + + private validateAllowedDecorators( + annotations: arkts.AnnotationUsage[], + otherDecorator: arkts.AnnotationUsage | undefined, + ): void { + annotations.forEach((annotation) => { + const decoratorName = getAnnotationName(annotation); + // rule1: misuse of decorator, only '@Builder' decorator allowed on global functions + if (allowedDecorators !== decoratorName || + (allowedDecorators === decoratorName && decoratorName.length > DECORATOR_LIMIT)) { + this.reportInvalidDecorator(annotation, otherDecorator); + } + }); + } + + private reportInvalidDecorator( + annotation: arkts.AnnotationUsage, + otherDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!otherDecorator) { + return; } - validateFunctionDecorator(node, context); - }, - }; - }, -}; + this.report({ + node: annotation, + message: this.messages.invalidDecorator, + fix: () => { + let startPosition = otherDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = otherDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: `` + }; + } + }); + } +} -export default rule; +export default OneDecoratorOnFunctionMethodRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a43242f045fe1d615d23666dd5a27f1d3f3a4f4 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts @@ -0,0 +1,411 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, TypeFlags, getIdentifierName, findDecorator, hasAnnotation } from '../utils/index'; + +const v1DecoratorMustHasType = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.WATCH, +]; + +const v2DecoratorMustHasType = [ + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.EVENT, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, +]; + +const propertyPropDecorator = [ + PresetDecorators.PROP_REF, + PresetDecorators.STORAGE_PROP_REF, +]; + +const SimpleTypesUnSupported = [ + TypeFlags.Boolean, + TypeFlags.String, + TypeFlags.Number, + TypeFlags.Enum, + TypeFlags.BigInt, +]; + +const ARRAY_TYPES = ['Array', 'Map', 'Set', 'Date']; + +const PropErrorType = ['Any', 'unknown']; + +class PropertyTypeRule extends AbstractUISyntaxRule { + private noObservedV2ClassNames: string[] = []; + private observedV2ClassNames: string[] = []; + private builderFunctionName: string[] = []; + private currentStructBuilderMethodName: string[] = []; + private structStaticMethodsMap: Map> = new Map(); + + public setup(): Record { + return { + propertyObjectLink: `'@ObjectLink' cannot be used with this type. Apply it only to classes decorated by '@Observed' or initialized using the return value of 'makeV1Observed'.`, + propertyProp: `The '@{{decoratorName}}' decorated attribute '{{propertyName}}' must be of the string, number, boolean, enum or object type.`, + propertyBuilderParam: `'@BuilderParam' property can only be initialized by '@Builder' function or '@Builder' method in struct.`, + }; + } + + public beforeTransform(): void { + this.noObservedV2ClassNames = []; + this.observedV2ClassNames = []; + this.builderFunctionName = []; + this.currentStructBuilderMethodName = []; + this.structStaticMethodsMap = new Map(); + } + + public parsed(node: arkts.AstNode): void { + this.collectObservedV1AndV2ClassNameAndEnumName(node); + this.validatePropertyTypeAndDecorators(node); + this.getBuilderFunctionNameAndStaticMethods(node); + this.getStructNameWithMultiplyBuilderParam(node); + } + + private collectObservedV1AndV2ClassNameAndEnumName( + node: arkts.AstNode, + ): void { + // Check if it's of type "Program". + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + // Traverse all child nodes of the Program + for (const child of node.getChildren()) { + // Check if it is of the ClassDeclaration type + if (arkts.isClassDeclaration(child) && child.definition) { + // Get a list of annotators + const annotations = child.definition.annotations; + + // Check for @ObservedV2 decorators + const observedV2Decorator = hasAnnotation(annotations, PresetDecorators.OBSERVED_V2); + + if (!observedV2Decorator && child.definition && child.definition.ident) { + // If there is a @Observed decorator, record the class name + const className = child.definition.ident.name; + this.noObservedV2ClassNames.push(className); + } + if (observedV2Decorator && child.definition && child.definition.ident) { + // If there is a @ObservedV2 decorator, record the class name + const v2ClassName = child.definition.ident.name; + this.observedV2ClassNames.push(v2ClassName); + } + } + } + } + + private areAllUnionMembersValid(unionType: arkts.ETSUnionType): boolean { + const members = unionType.types; + // At least one valid type All types must be allowed types and do not contain illegal combinations + let isValidType = false; + for (const member of members) { + if (arkts.isETSTypeReference(member) && + member.part && + member.part.name && + arkts.isIdentifier(member.part.name)) { + const propertyTypeName = member.part.name.name; + // If it's a simple type or ObservedV2, reject the entire union type outright + if (this.observedV2ClassNames.includes(propertyTypeName) || + SimpleTypesUnSupported.includes(propertyTypeName) || + PropErrorType.includes(propertyTypeName)) { + return false; + } + if (this.noObservedV2ClassNames.includes(propertyTypeName) || ARRAY_TYPES.includes(propertyTypeName)) { + isValidType = true; + } + } else if (arkts.isETSPrimitiveType(member) || + arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE || + arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE) { + return false; + } else if (arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE || + arkts.isETSUndefinedType(member)) { + continue; + } + } + return isValidType; + } + + private areAllUnionMembersNotAnyAndBigint(unionType: arkts.ETSUnionType): boolean { + const members = unionType.types; + for (const member of members) { + if (arkts.isETSTypeReference(member) && + member.part && + member.part.name && + arkts.isIdentifier(member.part.name)) { + const propertyTypeName = member.part.name.name; + if (propertyTypeName === PropErrorType[0] || propertyTypeName === TypeFlags.BigInt) { + return false; + } + } else if (arkts.nodeType(member) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE) { + return false; + } + } + return true; + } + + private validatePropertyTypeAndDecorators( + node: arkts.AstNode + ): void { + //Check whether the current node is a property + if (!arkts.isClassProperty(node) || !node.key || !arkts.isIdentifier(node.key)) { + return; + } + const propertyName = node.key.name; + // Gets the type of property + const propertyType = node.typeAnnotation; + const nodeKey = node.key; + const objectLinkDecorator = findDecorator(node, PresetDecorators.OBJECT_LINK); + // Determine whether the property has @Objectlink + if (objectLinkDecorator && propertyType) { + this.validateObjectLinkPropertyType(propertyType, nodeKey); + } + this.processPropertyAnnotations(node, propertyType, propertyName, nodeKey); + } + + private validateObjectLinkPropertyType( + propertyType: arkts.TypeNode, + nodeKey: arkts.Identifier + ): void { + if (arkts.isETSTypeReference(propertyType) && + propertyType.part && + propertyType.part.name && + arkts.isIdentifier(propertyType.part.name)) { + const propertyTypeName = propertyType.part.name.name; + if (this.observedV2ClassNames.includes(propertyTypeName) || + SimpleTypesUnSupported.includes(propertyTypeName) || + PropErrorType.includes(propertyTypeName)) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + } else if (arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE || + arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE || + arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE || + arkts.isETSPrimitiveType(propertyType) || + arkts.isETSUndefinedType(propertyType) + ) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + if (arkts.isETSUnionType(propertyType)) { + if (!this.areAllUnionMembersValid(propertyType)) { + this.report({ + node: nodeKey, + message: this.messages.propertyObjectLink, + }); + } + } + } + + private processPropertyAnnotations( + node: arkts.ClassProperty, + propertyType: arkts.TypeNode | undefined, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + // Iterate through all annotations + node.annotations?.forEach(annotation => { + if (!propertyType || !annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return; + } + const decoratorName = annotation.expr.name; + if (!arkts.isETSUnionType(propertyType)) { + this.reportIfInvalidPropType(propertyType, decoratorName, propertyName, nodeKey); + } else if (arkts.isETSUnionType(propertyType)) { + this.reportIfInvalidUnionPropType(propertyType, decoratorName, propertyName, nodeKey); + } + }); + } + + private reportIfInvalidPropType( + propertyType: arkts.TypeNode, + decoratorName: string, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + if (arkts.isETSTypeReference(propertyType) && + propertyType.part && + propertyType.part.name && + arkts.isIdentifier(propertyType.part.name)) { + const propertyTypeName = propertyType.part.name.name; + if ((propertyPropDecorator.includes(decoratorName)) && nodeKey) { + // Check if the @Prop property is of any or bigint + if (propertyTypeName === PropErrorType[0] || + propertyTypeName === TypeFlags.BigInt + ) { + this.report({ + node: nodeKey, + message: this.messages.propertyProp, + data: { + decoratorName, + propertyName, + }, + }); + } + } + } + } + + private reportIfInvalidUnionPropType( + propertyType: arkts.ETSUnionType, + decoratorName: string, + propertyName: string, + nodeKey: arkts.Identifier, + ): void { + if (!this.areAllUnionMembersNotAnyAndBigint(propertyType) && propertyPropDecorator.includes(decoratorName)) { + this.report({ + node: nodeKey, + message: this.messages.propertyProp, + data: { + decoratorName, + propertyName, + }, + }); + } + } + + private getStructNameWithMultiplyBuilderParam( + node: arkts.AstNode, + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + // Only the situation within the component is judged + if (!arkts.isStructDeclaration(member) || !member.definition.ident) { + return; + } + this.getCurrentStructBuilderMethodName(member); + member.definition?.body?.forEach((item) => { + this.checkBuilderParamInitialization(item); + }); + this.currentStructBuilderMethodName.length = 0; + }); + } + + private checkBuilderParamInitialization( + item: arkts.AstNode + ): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + const builderParamDecorator = findDecorator(item, PresetDecorators.BUILDER_PARAM); + if (item.value && !arkts.isMemberExpression(item.value) && !arkts.isIdentifier(item.value) && + builderParamDecorator) { + this.reportInvalidBuilderParamInitialized(item.key); + } else if (item.value && arkts.isMemberExpression(item.value) && builderParamDecorator) { + this.checkValidMemberExpressionUsage(item.value, item.key); + } else if (item.value && arkts.isIdentifier(item.value) && item.typeAnnotation && builderParamDecorator) { + if (arkts.isETSFunctionType(item.typeAnnotation) && + !this.builderFunctionName.includes(item.value.name)) { + this.reportInvalidBuilderParamInitialized(item.key); + } + } + } + + private checkValidMemberExpressionUsage( + itemValue: arkts.MemberExpression, + itemKey: arkts.Expression + ): void { + if (!arkts.isThisExpression(itemValue.object) && !arkts.isIdentifier(itemValue.object)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } else if (arkts.isThisExpression(itemValue.object) && + arkts.isIdentifier(itemValue.property) && + !this.currentStructBuilderMethodName.includes(itemValue.property.name)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } else if (arkts.isIdentifier(itemValue.object) && arkts.isIdentifier(itemValue.property)) { + const structName = getIdentifierName(itemValue.object); + const staticMethodsName = getIdentifierName(itemValue.property); + const methods = this.structStaticMethodsMap.get(structName); + if (methods && !methods.has(staticMethodsName)) { + this.reportInvalidBuilderParamInitialized(itemKey); + } + } + } + + private reportInvalidBuilderParamInitialized(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.propertyBuilderParam, + }); + } + + private getBuilderFunctionNameAndStaticMethods( + node: arkts.AstNode + ): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (arkts.isFunctionDeclaration(member) && member.scriptFunction.id?.name) { + const hasBuilderDecorator = findDecorator(member, PresetDecorators.BUILDER); + if (hasBuilderDecorator) { + this.builderFunctionName.push(member.scriptFunction.id?.name); + } + } + if (arkts.isStructDeclaration(member) && member.definition.ident) { + let structName = member.definition.ident.name; + this.structStaticMethodsMap.set(structName, new Set()); + member.definition.body.forEach((item) => { + this.collectBuilderStaticMethodFromStruct(item, structName); + }); + } + }); + } + + private collectBuilderStaticMethodFromStruct( + item: arkts.AstNode, + structName: string + ): void { + if (!arkts.isMethodDefinition(item) || !item.scriptFunction.id || !item.isStatic) { + return; + } + const hasBuilderDecorator = findDecorator(item.scriptFunction, PresetDecorators.BUILDER); + // judgment static method + if (hasBuilderDecorator && arkts.isIdentifier(item.scriptFunction.id) && item.isStatic) { + const methodName = item.scriptFunction.id.name; + this.structStaticMethodsMap.get(structName)?.add(methodName); + } + } + + private getCurrentStructBuilderMethodName( + node: arkts.StructDeclaration + ): void { + node.definition?.body?.forEach((item) => { + if (!arkts.isMethodDefinition(item) || !item.scriptFunction.id) { + return; + } + const builderDecorator = findDecorator(item.scriptFunction, PresetDecorators.BUILDER); + // judgment static method + if (builderDecorator && arkts.isIdentifier(item.scriptFunction.id) && !item.isStatic) { + this.currentStructBuilderMethodName.push(item.scriptFunction.id.name); + } + }); + } +} + +export default PropertyTypeRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts b/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd3abf59c2bf2f2f77c28ff6fc4521a3603f6bdf --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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 { getClassPropertyAnnotationNames, PresetDecorators, isPrivateClassProperty, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class RequireDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidPrivateWithRequire: `Property '{{propertyName}}' can not be decorated with both {{decoratorName}} and private.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + this.checkRequireDecorator(node); + } + + private checkRequireDecorator(node: arkts.StructDeclaration): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + // Get the list of decorators applied to the class property + const propertyDecorators = getClassPropertyAnnotationNames(member); + if (!propertyDecorators.includes(PresetDecorators.REQUIRE)) { + return; + } + if (isPrivateClassProperty(member)) { + this.handlePrivateWithRequire(member); + } + }); + } + + private handlePrivateWithRequire(member: arkts.ClassProperty): void { + const requireDecorator = findDecorator(member, PresetDecorators.REQUIRE); + const propertyName = getClassPropertyName(member); + if (!propertyName) { + return; + } + if (requireDecorator) { + this.report({ + node: requireDecorator, + message: this.messages.invalidPrivateWithRequire, + data: { + propertyName, + decoratorName: PresetDecorators.REQUIRE, + }, + }); + } + } +} + +export default RequireDecoratorRegularRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..57901a88dfde794231e4ce14ecf7e125cfe9935c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/reusable-component-in-V2-check.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getAnnotationUsage, PresetDecorators } from '../utils'; + +class ReusableComponentInV2CheckRule extends AbstractUISyntaxRule { + private reusableStructName: string[] = []; + + public setup(): Record { + return { + noReusableV1InComponentV2: `When a custom component is decorated with @ComponentV2 and contains a child component decorated with @Reusable, the child component will not create.`, + }; + } + + public beforeTransform(): void { + this.reusableStructName = []; + } + public parsed(node: arkts.StructDeclaration): void { + this.initStructName(node); + this.checkNoReusableV1InComponentV2(node); + } + + private initStructName(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + const reusableV1Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V1); + const structName = childNode.definition?.ident?.name; + if (reusableV1Decorator && structName) { + this.reusableStructName.push(structName); + } + } + } + + private checkNoReusableV1InComponentV2(node: arkts.AstNode,): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableStructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let structNode: arkts.AstNode = node; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + const annotationsList = structNode.definition.annotations; + // Check that the current component is decorated by the @ComponentV2 decorator + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V2)) { + this.report({ + node: node, + message: this.messages.noReusableV1InComponentV2, + }); + } + } + } +} + +export default ReusableComponentInV2CheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..24a7ca87ced670eeea904ec783d851b98cdb357d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/reusableV2-decorator-check.ts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 { PresetDecorators, getAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ReusableV2DecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + conflictingDecorators: `The '@Reusable' and '@ReusableV2' annotations cannot be applied simultaneously.`, + invalidDecoratorUsage: `@ReusableV2 is only applicable to custom components decorated by @ComponentV2.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; + } + const structNode = node.definition.ident; + // Check whether the decoration exists, and mark true if it does + const reusableDecoratorUsage = getAnnotationUsage(node, PresetDecorators.REUSABLE_V1); + const reusableV2DecoratorUsage = getAnnotationUsage(node, PresetDecorators.REUSABLE_V2); + const componentV2DecoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + + // Check whether @Reusable and @ReusableV2 exist at the same time + if (reusableV2DecoratorUsage && reusableDecoratorUsage && structNode) { + this.reportConflictingDecorators(reusableDecoratorUsage, structNode); + } + + // Check if @ReusableV2 is applied to a class decorated by @ComponentV2 + if (reusableV2DecoratorUsage && !componentV2DecoratorUsage && structNode) { + this.reportInvalidDecoratorUsage(node, structNode); + } + } + + private reportConflictingDecorators( + reusableDecoratorUsage: arkts.AnnotationUsage | undefined, + structNode: arkts.Identifier | undefined, + ): void { + if (!structNode || !reusableDecoratorUsage) { + return; + } + this.report({ + node: structNode, + message: this.messages.conflictingDecorators, + }); + } + + private reportInvalidDecoratorUsage( + node: arkts.StructDeclaration, + structNode: arkts.Identifier | undefined, + ): void { + if (!structNode || !node) { + return; + } + this.report({ + node: structNode, + message: this.messages.invalidDecoratorUsage, + }); + } +} + +export default ReusableV2DecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts index 773b133ac3fad557e76e0c22fc4ec61f9201487f..509a2a2fd79163578377eb4f433b10a1d59d3f06 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts @@ -14,94 +14,108 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators, getAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, getAnnotationUsage, ReuseConstants } from '../utils'; -function findStructsWithReusableAndComponentV2(node: arkts.AstNode, reusableV2ComponentV2Struct: string[]): void { - //Go through all the children of Program - for (const childNode of node.getChildren()) { - // Check whether the type is struct - if (!arkts.isStructDeclaration(childNode)) { - continue; - } - // Check that the current component has @ComponentV2 and @ReusableV2 decorators - const hasReusableV2Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V2); - const hasComponentV2Decorator = getAnnotationUsage(childNode, PresetDecorators.COMPONENT_V2); - if (hasReusableV2Decorator && hasComponentV2Decorator) { - const struceName = childNode.definition?.ident?.name ?? ''; - reusableV2ComponentV2Struct.push(struceName); - } - } -} +class ReuseAttributeCheckRule extends AbstractUISyntaxRule { + private reusableV2ComponentV2Struct: string[] = []; -function validateReuseOrReuseIdUsage(context: UISyntaxRuleContext, node: arkts.MemberExpression, - reusableV2ComponentV2Struct: string[]): void { - const structNode = node.object; - // Gets the reuse or reuseId attribute - const decoratedNode = node.property; - if (arkts.isCallExpression(structNode)) { - const Node = structNode.expression; - if (decoratedNode.dumpSrc() === 'reuse' && !reusableV2ComponentV2Struct.includes(Node.dumpSrc())) { - reportInvalidReuseUsage(context, node, structNode, rule); + public setup(): Record { + return { + invalidReuseUsage: `The reuse attribute is only applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, + invalidReuseIdUsage: `The reuseId attribute is not applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, + }; } - else if (decoratedNode.dumpSrc() === 'reuseId' && reusableV2ComponentV2Struct.includes(Node.dumpSrc())) { - reportInvalidReuseIdUsage(context, node, structNode, rule); - } - } -} - -function reportInvalidReuseUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode, - rule: any): void { - context.report({ - node: node, - message: rule.messages.invalidReuseUsage, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: `${structNode.dumpSrc()}.reuseId`, - }; - }, - }); -} -function reportInvalidReuseIdUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode, - rule: any): void { - context.report({ - node: node, - message: rule.messages.invalidReuseIdUsage, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: `${structNode.dumpSrc()}.reuse`, - }; - }, - }); -} + public beforeTransform(): void { + this.reusableV2ComponentV2Struct = []; + } -const rule: UISyntaxRule = { - name: 'reuse-attribute-check', - messages: { - invalidReuseUsage: `The reuse attribute is only applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, - invalidReuseIdUsage: `The reuseId attribute is not applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`, - }, - setup(context) { - const reusableV2ComponentV2Struct: string[] = []; - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check whether the type is "Program" if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - findStructsWithReusableAndComponentV2(node, reusableV2ComponentV2Struct); + this.findStructsWithReusableAndComponentV2(node); } if (arkts.isMemberExpression(node)) { - validateReuseOrReuseIdUsage(context, node, reusableV2ComponentV2Struct); + this.validateReuseOrReuseIdUsage(node); + } + } + + private findStructsWithReusableAndComponentV2(node: arkts.AstNode): void { + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + // Check that the current component has @ComponentV2 and @ReusableV2 decorators + const reusableV2Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V2); + const componentV2Decorator = getAnnotationUsage(childNode, PresetDecorators.COMPONENT_V2); + if (reusableV2Decorator && componentV2Decorator) { + const struceName = childNode.definition.ident?.name ?? ''; + this.reusableV2ComponentV2Struct.push(struceName); + } } - }, - }; - }, + } + + private validateReuseOrReuseIdUsage( + node: arkts.MemberExpression + ): void { + const structNode = node.object; + // Gets the reuse or reuseId attribute + const decoratedNode = node.property; + if (arkts.isCallExpression(structNode)) { + const nodeExpression = structNode.expression; + if (arkts.isIdentifier(nodeExpression) && arkts.isIdentifier(decoratedNode)) { + if (decoratedNode.name === ReuseConstants.REUSE && + !this.reusableV2ComponentV2Struct.includes(nodeExpression.name)) { + this.reportInvalidReuseUsage(node, decoratedNode); + } + else if (decoratedNode.name === ReuseConstants.REUSE_ID && + this.reusableV2ComponentV2Struct.includes(nodeExpression.name)) { + this.reportInvalidReuseIdUsage(node, decoratedNode); + } + } + } + } + + private reportInvalidReuseUsage( + node: arkts.AstNode, + structNode: arkts.AstNode, + ): void { + this.report({ + node: node, + message: this.messages.invalidReuseUsage, + fix: () => { + const startPosition = structNode.startPosition; + const endPosition = structNode.endPosition; + return { + title: 'Change reuse to reuseId', + range: [startPosition, endPosition], + code: ReuseConstants.REUSE_ID, + }; + }, + }); + } + + private reportInvalidReuseIdUsage( + node: arkts.AstNode, + structNode: arkts.AstNode, + ): void { + this.report({ + node: node, + message: this.messages.invalidReuseIdUsage, + fix: () => { + const startPosition = structNode.startPosition; + const endPosition = structNode.endPosition; + return { + title: 'Change reuseId to reuse', + range: [startPosition, endPosition], + code: ReuseConstants.REUSE, + }; + }, + }); + } }; -export default rule; \ No newline at end of file +export default ReuseAttributeCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts b/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts new file mode 100644 index 0000000000000000000000000000000000000000..aace72ff3493ab6e4a8d4b765f64dc0282270519 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/specific-component-children.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) 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 { + getIdentifierName, + PresetDecorators, + SINGLE_CHILD_COMPONENT, + TOGGLE_TYPE, + ToggleType, + TYPE +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class SpecificComponentChildrenRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + toggleTypeCheckboxWithNoChild: `When the component '{{componentName}}' set '{{toggleTypeKey}}' as '{{toggleTypeValue}}', it can't have any child.`, + toggleTypeButtonWithSingleChild: `When the component '{{componentName}}' set '{{toggleTypeKey}}' as '{{toggleTypeValue}}', it can only have a single child component.`, + }; + } + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + // Check whether the current node is an identifier and toggle component + if (!arkts.isIdentifier(node.expression)) { + return; + } + const componentName: string = getIdentifierName(node.expression); + if (componentName !== PresetDecorators.TOGGLE) { + return; + } + const toggleTypeValue = this.getToggleType(node); + if (toggleTypeValue === '') { + return; + } + // If there is more than one subComponent in the BlockStatement, an error is reported + node.getChildren().forEach(member => { + if (!arkts.isBlockStatement(member)) { + return; + } + // If the toggle component type is checkbox and has child components, an error is reported + if (toggleTypeValue === ToggleType.CHECKBOX && member.statements.length > 0) { + this.report({ + node: node, + message: this.messages.toggleTypeCheckboxWithNoChild, + data: { + componentName: componentName, + toggleTypeKey: TYPE, + toggleTypeValue: toggleTypeValue + } + }); + } + // If the toggle component type is button and there is more than one child component, an error is reported + if (toggleTypeValue === ToggleType.BUTTON && member.statements.length > SINGLE_CHILD_COMPONENT) { + this.report({ + node: node, + message: this.messages.toggleTypeButtonWithSingleChild, + data: { + componentName: componentName, + toggleTypeKey: TYPE, + toggleTypeValue: toggleTypeValue + } + }); + } + }); + } + + private getToggleType(node: arkts.CallExpression): string { + let toggleType = ''; + node.arguments.forEach((member) => { + if (!arkts.isObjectExpression(member) || !member.properties) { + return; + } + member.properties.forEach((property) => { + if (!arkts.isProperty(property) || !property.value) { + return; + } + // If the property name is not 'toggle type' + if (!arkts.isMemberExpression(property.value) || !property.value.object || + !arkts.isIdentifier(property.value.object) || property.value.object.name !== TOGGLE_TYPE) { + return; + } + if (!property.value.property || !arkts.isIdentifier(property.value.property)) { + return; + } + toggleType = property.value.property.name; + }); + }); + return toggleType; + } +} + +export default SpecificComponentChildrenRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f2fdb8e2653c2e9e6376394afd5b1e5d863dba7 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) 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 { getClassPropertyName, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class StaticParamRequireRule extends AbstractUISyntaxRule { + private staticPropertyMap: Map = new Map(); + + public setup(): Record { + return { + cannotInitializePrivateVariables: `Static property '{{propertyName}}' can not be initialized through the component constructor.`, + }; + } + + public beforeTransform(): void { + this.staticPropertyMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + // Check if the current node is the root node + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || !member.definition.ident.name) { + return; + } + const hasComponentV1 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V2); + const structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.addStaticProperty(item, structName, hasComponentV1, hasComponentV2); + }); + }); + } + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // If the initialization is for a component with private properties + if (!this.staticPropertyMap.has(componentName)) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName: string = property.key.name; + if (this.staticPropertyMap.get(componentName)!.includes(propertyName)) { + this.report({ + node: property, + message: this.messages.cannotInitializePrivateVariables, + data: { + propertyName: propertyName, + }, + }); + } + }); + }); + } + + private addStaticProperty( + item: arkts.AstNode, + structName: string, + hasComponentV1: boolean, + hasComponentV2: boolean + ): void { + if (!arkts.isClassProperty(item) || !item.isStatic) { + return; + } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } + // Static properties with decorators in componentV2 need to be checked + if (hasComponentV2 && item.annotations.length > 0) { + this.addElement(structName, propertyName); + } + // Static properties in componentV1 need to be verified + if (hasComponentV1 && !hasComponentV2) { + this.addElement(structName, propertyName); + } + } + + private addElement(structName: string, propertyName: string): void { + // Check if structName already exists in privateMap + if (this.staticPropertyMap.has(structName)) { + // If it exists, retrieve the current string[] and append the new content + this.staticPropertyMap.get(structName)!.push(propertyName); + } else { + // If it doesn't exist, create a new string[] and add the content + this.staticPropertyMap.set(structName, [propertyName]); + } + } +} + +export default StaticParamRequireRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts index 7ca8e7b3a619c974d1eea7dae318313ff951ff47..11b48a21938957acdad946cfc2bf4c112ea0236a 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts @@ -15,60 +15,45 @@ import * as arkts from '@koalaui/libarkts'; import { getAnnotationUsage, PresetDecorators } from '../utils'; -import { UISyntaxRule } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const rule: UISyntaxRule = { - name: 'struct-missing-decorator', - messages: { - missingComponentDecorator: `struct '{{structName}}' is missing '@Component' or '@CustomDialog' decorators`, - misusedPreview: `struct '{{structName}}' misused '@Preview' decorator. it should be decorated by '@Component' or '@CustomDialog'`, - misusedObserved: `struct '{{structName}}' misused '@Observed' decorator. it should be decorated by '@Component' or '@CustomDialog'`, - }, - setup(context) { - function hasDecorator(node: arkts.StructDeclaration, decorator: string): boolean { - return !!getAnnotationUsage(node, decorator); +class StructMissingDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingComponentDecorator: `Annotation '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '{{structName}}'.` + }; } - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; + } + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; } // Check for the presence of specific decorators on the struct const structName = node.definition.ident?.name ?? ''; const structNode = node.definition.ident; - const hasComponent = hasDecorator(node, PresetDecorators.COMPONENT_V1); - const hasComponentV2 = hasDecorator(node, PresetDecorators.COMPONENT_V2); - const hasCustomDialog = hasDecorator(node, PresetDecorators.CUSTOM_DIALOG); - const hasPreview = getAnnotationUsage(node, PresetDecorators.PREVIEW); - const hasObserved = getAnnotationUsage(node, PresetDecorators.OBSERVED_V1); + const hasComponent = this.hasDecorator(node, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = this.hasDecorator(node, PresetDecorators.COMPONENT_V2); + const hasCustomDialog = this.hasDecorator(node, PresetDecorators.CUSTOM_DIALOG); + // If no valid component decorators (@Component or @CustomDialog) are found if (!hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.missingComponentDecorator, - data: { structName }, - }); - } - // If the @Preview decorator is used but the struct is not decorated with @Component or @CustomDialog - if (hasPreview && !hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.misusedPreview, - data: { structName }, - }); + this.report({ + node: structNode, + message: this.messages.missingComponentDecorator, + data: { structName }, + }); } - // If the @Observed decorator is used but the struct is not decorated with @Component or @CustomDialog - if (hasObserved && !hasComponent && !hasComponentV2 && !hasCustomDialog && structNode) { - context.report({ - node: structNode, - message: rule.messages.misusedObserved, - data: { structName }, - }); - } - }, - }; - }, -}; + } + + private hasDecorator(node: arkts.StructDeclaration, decorator: string): boolean { + return !!getAnnotationUsage(node, decorator); + } +} -export default rule; +export default StructMissingDecoratorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts similarity index 32% rename from arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts rename to arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts index cbfeef0c308b74d99a45959d0b4c2e4b4263ebb4..12945ee48719f9628d9f606b52d2483e71268369 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/local-builder-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts @@ -1,7 +1,7 @@ /* * Copyright (c) 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 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 @@ -14,50 +14,29 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -function checkLocalBuilder(node: arkts.ClassDeclaration, context: UISyntaxRuleContext): void { - node.definition?.body.forEach(body => { - if (!arkts.isMethodDefinition(body)) { - return; - } - const localBuilder = body.scriptFunction?.annotations?.find( - annotation => annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.LOCAL_BUILDER); - if (!localBuilder) { - return; - } - context.report({ - node: localBuilder, - message: rule.messages.invalidUsage, - fix: (localBuilder) => { - const startPosition = arkts.getStartPosition(localBuilder); - const endPosition = arkts.getEndPosition(localBuilder); +class StructNoExtendsRule extends AbstractUISyntaxRule { + public setup(): Record { return { - range: [startPosition, endPosition], - code: '', + structNoExtends: `Structs are not allowed to inherit from classes or implement interfaces.`, }; - }, - }); - }); -} - -const rule: UISyntaxRule = { - name: 'local-builder-check', - messages: { - invalidUsage: `The '@LocalBuilder' decorator can only be used in 'struct'.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isClassDeclaration(node)) { - return; + } + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node) || !node.definition.ident) { + return; + } + const hasSuperClass: boolean = node.definition.super !== undefined; + const hasImplements: boolean = node.definition.implements.length > 0; + // If there is an inheritance class or implementation interface, an error is reported + if (hasSuperClass || hasImplements) { + const errorNode = node.definition.ident; + this.report({ + node: errorNode, + message: this.messages.structNoExtends, + }); } - checkLocalBuilder(node, context); - }, - }; - }, -}; + } +} -export default rule; +export default StructNoExtendsRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts index 9aaa53de01b43f3b6dcf7be32c17dd39156f6a7e..81228846b8fd8c0f49abb19b890c95ffe39529c6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts @@ -14,39 +14,106 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -function checkInvalidStaticPropertyDecorations(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - node.definition.body.forEach((member) => { - // Errors are reported when the node type is ClassProperty, - if (arkts.isClassProperty(member)) { - const propertyNameNode = member.key; - if ((member.isStatic && getClassPropertyAnnotationNames(member).length > 0) && propertyNameNode) { - context.report({ - node: propertyNameNode, - message: rule.messages.invalidStaticUsage - }); - } +import { getClassPropertyAnnotationNames, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const v1Decorators: string[] = [ + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.STORAGE_LINK, + PresetDecorators.WATCH, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.REQUIRE, +]; + +const v2Decorators: string[] = [ + PresetDecorators.PARAM, + PresetDecorators.ONCE, + PresetDecorators.EVENT, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.MONITOR, + PresetDecorators.REQUIRE, +]; + +class StructPropertyDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidStaticUsage: `The static variable of struct cannot be used together with built-in annotations.` + }; } - }); -} -const rule: UISyntaxRule = { - name: 'struct-property-decorator', - messages: { - invalidStaticUsage: `Static variables in custom components cannot be decorated by built-in variable decorators.` - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + const hasComponentV1 = hasAnnotation(node.definition.annotations, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = hasAnnotation(node.definition.annotations, PresetDecorators.COMPONENT_V2); + this.checkInvalidStaticPropertyDecorations(node, hasComponentV1, hasComponentV2); + if (hasComponentV2) { + this.checkInvalidStaticMethodDecorations(node); + } + } + if (arkts.isClassDeclaration(node)) { + this.checkInvalidStaticMethodDecorations(node); } - checkInvalidStaticPropertyDecorations(context, node); - }, - }; - }, -}; + } + + private hasPropertyDecorator( + member: arkts.ClassProperty, + decorators: String[] + ): boolean { + const annotationName = getClassPropertyAnnotationNames(member); + return decorators.some(decorator => + annotationName.includes(decorator) + ); + } + + private checkInvalidStaticPropertyDecorations( + node: arkts.StructDeclaration, + hasComponentV1: boolean, + hasComponentV2: boolean + ): void { + node.definition.body.forEach((member) => { + // Errors are reported when the node type is static ClassProperty, + if (!arkts.isClassProperty(member) || !member.key || !member.isStatic) { + return; + } + if (hasComponentV1 && this.hasPropertyDecorator(member, v1Decorators) || + hasComponentV2 && this.hasPropertyDecorator(member, v2Decorators)) { + const propertyNameNode = member.key; + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); + } + }); + } + + private checkInvalidStaticMethodDecorations(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { + node.definition?.body.forEach((member) => { + // Errors are reported when the node type is static Method, + if (!arkts.isMethodDefinition(member) || !member.name || !member.isStatic) { + return; + } + const hasMonitor = member.funcExpr.scriptFunction.annotations.some(annotation => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return false; + } + return annotation.expr.name === PresetDecorators.MONITOR; + }); + if (!hasMonitor) { + return; + } + const propertyNameNode = member.name; + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); + }); + } +} -export default rule; \ No newline at end of file +export default StructPropertyDecoratorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts new file mode 100644 index 0000000000000000000000000000000000000000..899df9874574b7b907bcde59e9989e1034b60e4c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-optional.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, isClassPropertyOptional } from '../utils/index'; + +// @Prop needs to consider whether there is an initialization value +const requireDecorators = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, +]; + +class StructPropertyOptionalRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + propertyOptional: `The '{{decoratorName}}' property '{{propertyName}}' cannot be an optional parameter.`, + }; + } + + public parsed(node: arkts.AstNode): void { + // Check if it's a class property + if (!arkts.isClassProperty(node)) { + return; + } + if (!node.key || !arkts.isIdentifier(node.key)) { + return; + } + const keyname = node.key.name; + // If the property is optional, check the decorator further + if (!isClassPropertyOptional(node)) { + return; + } + this.hasPropOrRequireDecorator(node, keyname); + } + + private hasPropOrRequireDecorator(node: arkts.ClassProperty, propertyName: string): void { + node.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const decoratorName = annotation.expr?.name; + const nodeKey = node.key; + const nodeValue = node.value; + // Check whether the prop decorator has an initial value, and no alarm will be generated if there is an initial value + if (decoratorName === PresetDecorators.PROP_REF && nodeKey && !nodeValue) { + this.report({ + node: nodeKey, + message: this.messages.propertyOptional, + data: { + decoratorName, + propertyName, + }, + }); + } else if (requireDecorators.includes(decoratorName) && nodeKey) { + this.report({ + node: nodeKey, + message: this.messages.propertyOptional, + data: { + decoratorName, + propertyName, + }, + }); + } + } + }); + } +} + +export default StructPropertyOptionalRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts index 63c06587f6aff660befe92e7c0ace646ec249363..38cbf5e3f29a4893b1026a6a7516bae5a0b62ec2 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts @@ -14,59 +14,95 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, findDecorator } from '../utils'; // A list of decorators that needs to be initialized locally -const mustInitializeDecorators = ['State', 'StorageLink', 'StorageProp', 'LocalStorageLink', 'LocalStorageProp', - 'Provide']; +const mustInitializeDecorators = [ + PresetDecorators.STATE, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.PROVIDE, +]; // Disables a list of decorators that are initialized locally -const prohibitInitializeDecorators = ['Link', 'Consume', 'ObjectLink']; +const prohibitInitializeDecorators = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, +]; -function reportVariablesInitializationError(context: UISyntaxRuleContext, node: arkts.ClassProperty): void { - // Check whether the value field exists and whether it has been initialized - const valueExists = !!node.value; - // Iterate through each decorator and verify the initialization rules - node.annotations.forEach(annotation => { - const decoratorName = annotation.expr?.dumpSrc() ?? ''; - if (mustInitializeDecorators.includes(decoratorName)) { - // If it is a decorator that needs to be initialized - if (!valueExists) { - // If there is no initialization expression and there is no @Require, an error is reported - context.report({ - node: annotation, - message: rule.messages.mustBeInitializedLocally, - }); - } - } else if (prohibitInitializeDecorators.includes(decoratorName)) { - // If it is a decorator that prohibits initialization - if (valueExists) { - // If an initialization expression exists, an error is reported - context.report({ - node: annotation, - message: rule.messages.prohibitLocalInitialization, - }); - } +// When used with @Require, non-initialization is allowed +const requireCanReleaseMandatoryDecorators = [ + PresetDecorators.STATE, + PresetDecorators.PROVIDE, +]; + +class StructVariableInitializationRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + mustBeInitializedLocally: `The '@{{decoratorName}}' property must be specified a default value.`, + prohibitLocalInitialization: `The '@{{decoratorName}}' property cannot be specified a default value.`, + }; } - }); -} -const rule: UISyntaxRule = { - name: 'struct-variable-initialization', - messages: { - mustBeInitializedLocally: `Variables decorated by '@State', '@StorageLink', '@StorageProp', '@LocalStorageLink', '@LocalStorageProp' and '@Provide' must be initialized locally.`, - prohibitLocalInitialization: `Variables decorated by '@Link', '@Consume', and '@ObjectLink' cannot be initialized locally.` - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { // Check if the current node is a class attribute if (!arkts.isClassProperty(node)) { - return; + return; + } + this.reportVariablesInitializationError(node); + } + + private reportVariablesInitializationError(node: arkts.ClassProperty): void { + // Check whether the value field exists and whether it has been initialized + const valueExists = !!node.value; + // Check for the presence of require decorator + const hasRequire = findDecorator(node, PresetDecorators.REQUIRE); + node.annotations.some(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + mustInitializeDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.reportInitializeError(decoratorName, valueExists, annotation, hasRequire); + return true; + } else if (annotation.expr && arkts.isIdentifier(annotation.expr) && + prohibitInitializeDecorators.includes(annotation.expr.name)) { + const decoratorName = annotation.expr.name; + this.reportProHibitInitializeError(decoratorName, valueExists, annotation); + return true; + } + return false; + }); + } + + private reportInitializeError(decoratorName: string, valueExists: boolean, + annotation: arkts.AnnotationUsage, hasRequire: arkts.AnnotationUsage | undefined): void { + // Used with @require allows non-initialization + if (hasRequire && requireCanReleaseMandatoryDecorators.includes(decoratorName)) { + return; + } + // If it is a decorator that needs to be initialized + if (!valueExists) { + // If there is no initialization expression and there is no @Require, an error is reported + this.report({ + node: annotation, + message: this.messages.mustBeInitializedLocally, + data: { decoratorName }, + }); + } + } + + private reportProHibitInitializeError(decoratorName: string, valueExists: boolean, + annotation: arkts.AnnotationUsage): void { + // If it is a decorator that prohibits initialization + if (valueExists) { + // If an initialization expression exists, an error is reported + this.report({ + node: annotation, + message: this.messages.prohibitLocalInitialization, + data: { decoratorName }, + }); } - reportVariablesInitializationError(context, node); - } - }; - } -}; + } +} -export default rule; \ No newline at end of file +export default StructVariableInitializationRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts index db909687b0635ef60fb91ca85e451d374e386733..a585241ece44e81b987776a2e9a59610114226a7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts @@ -14,133 +14,130 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { PresetDecorators, findDecorator, getClassDeclarationAnnotation, getAnnotationUsage } from '../utils/index'; -const rule: UISyntaxRule = { - name: 'track-decorator-check', - messages: { - invalidTarget: `The '@Track' decorator can only be used on class member variables.`, - invalidClass: `The '@Track' decorator can only be used within a 'class' decorated with '@Observed'.` - }, - setup(context) { - return { - parsed: (node): void => { +class TrackDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + trackOnClassMemberOnly: `The '@Track' annotation can decorate only member variables of a class.`, + trackMustUsedWithObserved: `'@Track' cannot be used with classes decorated by '@ObservedV2'. Use the '@Trace' annotation instead.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + this.reportInvalidTrackDecoratorUsage(node); + } if (arkts.isStructDeclaration(node)) { - checkInvalidTrackAnnotations(context, node); + this.checkInvalidTrackAnnotations(node); } // Check if the current node is a class declaration if (arkts.isClassDeclaration(node)) { - checkTrackOnlyUsedWithObserved(context, node); + this.checkTrackUsedWithObservedV2(node); } - } - }; - } -}; - -function checkInvalidTrackAnnotations(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { - // Traverse all members of the struct body - node.definition.body.forEach((member) => { - // Check whether it is a member variable - if (arkts.isClassProperty(member)) { - const hasTrackDecorator = findClassPropertyAnnotation(member, PresetDecorators.TRACK); - // If a member variable is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } } - // Check whether this is the method - if (arkts.isMethodDefinition(member)) { - const hasTrackDecorator = getMethodAnnotation(member, PresetDecorators.TRACK); - // If the method is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } - } - },); -} -function checkTrackOnlyUsedWithObserved(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { - // Check if the class is decorated with @Observed - const hasObservedDecorator = node.definition?.annotations?.find( - annotations => - annotations.expr && - arkts.isIdentifier(annotations.expr) && - annotations.expr.name === PresetDecorators.OBSERVED_V1 - ); - // Traverse all members of the body class - node.definition?.body.forEach((member) => { - // Check whether it is a class attribute - if (arkts.isClassProperty(member)) { - const hasTrackDecorator = findClassPropertyAnnotation(member, PresetDecorators.TRACK); - // If the class is not decorated with @Observed and has decorators, an error is reported - if (!hasObservedDecorator && hasTrackDecorator) { - reportInvalidClass(context, hasTrackDecorator); - } + private reportInvalidTrackDecoratorUsage( + node: arkts.FunctionDeclaration | arkts.VariableDeclaration | arkts.ScriptFunction | + arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration + ): void { + const trackDecorator = findDecorator(node, PresetDecorators.TRACK); + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } } - // Check whether this is the method - if (arkts.isMethodDefinition(member)) { - const hasTrackDecorator = getMethodAnnotation(member, PresetDecorators.TRACK); - // If the method is decorated with @Track, an error is reported immediately - if (hasTrackDecorator) { - reportInvalidTarget(context, hasTrackDecorator); - } + + private checkInvalidTrackAnnotations(node: arkts.StructDeclaration): void { + const trackAnnotation = getAnnotationUsage(node, PresetDecorators.TRACK); + if (trackAnnotation) { + this.reportInvalidTarget(trackAnnotation); + } + // Traverse all members of the struct body + node.definition.body.forEach((member) => { + // Check whether it is a member variable + if (arkts.isClassProperty(member)) { + const trackDecorator = findDecorator(member, PresetDecorators.TRACK); + // If a member variable is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + } + // Check whether this is the method + if (arkts.isMethodDefinition(member)) { + const trackDecorator = findDecorator(member.scriptFunction, PresetDecorators.TRACK); + // If the method is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + } + },); } - }); -} -function reportInvalidClass(context: UISyntaxRuleContext, hasTrackDecorator: arkts.AnnotationUsage): void { - context.report({ - node: hasTrackDecorator, - message: rule.messages.invalidClass, - fix: (hasTrackDecorator) => { - const startPosition = arkts.getStartPosition(hasTrackDecorator); - const endPosition = arkts.getEndPosition(hasTrackDecorator); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); -} + private checkTrackUsedWithObservedV2(node: arkts.ClassDeclaration): void { + // Check if the struct is decorated with @Observed + const observedV2Decorator = getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V2); -function getMethodAnnotation( - node: arkts.MethodDefinition, - annotationName: string) - : arkts.AnnotationUsage | undefined { - return node.scriptFunction.annotations?.find( - annotation => - annotation.expr && - annotation.expr.dumpSrc() === annotationName - ); -} + // Traverse all members of the body class + node.definition?.body.forEach((member) => { + // Check whether it is a class attribute + if (arkts.isClassProperty(member)) { + const trackDecorator = findDecorator(member, PresetDecorators.TRACK); + // If the class is not decorated with @Observed and has decorators, an error is reported + if (observedV2Decorator && trackDecorator) { + this.reportInvalidClass(trackDecorator); + } + } + // Check whether this is the method + if (arkts.isMethodDefinition(member)) { + const trackDecorator = findDecorator(member.scriptFunction, PresetDecorators.TRACK); + // If the method is decorated with @Track, an error is reported immediately + if (trackDecorator) { + this.reportInvalidTarget(trackDecorator); + } + if (observedV2Decorator && trackDecorator) { + this.reportInvalidClass(trackDecorator); + } + } + }); + } -function findClassPropertyAnnotation( - node: arkts.ClassProperty, - annotationName: string) - : arkts.AnnotationUsage | undefined { - return node.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === annotationName - ); -} + private reportInvalidClass(trackDecorator: arkts.AnnotationUsage): void { + this.report({ + node: trackDecorator, + message: this.messages.trackMustUsedWithObserved, + fix: (trackDecorator) => { + let startPosition = trackDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = trackDecorator.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } -function reportInvalidTarget( - context: UISyntaxRuleContext, - node: arkts.AnnotationUsage) - : void { - context.report({ - node: node, - message: rule.messages.invalidTarget, - fix: (node) => { - const startPosition = arkts.getStartPosition(node); - const endPosition = arkts.getEndPosition(node); - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + private reportInvalidTarget(node: arkts.AnnotationUsage): void { + this.report({ + node: node, + message: this.messages.trackOnClassMemberOnly, + fix: (node) => { + let startPosition = node.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = node.endPosition; + return { + title: 'Remove the annotation', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } } -export default rule; \ No newline at end of file +export default TrackDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts deleted file mode 100644 index 79c60be2e1557b43ad9d29e255dc5fb61f60d7fc..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 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 { getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -function findTypeDecorator( - annotations: readonly arkts.AnnotationUsage[] -): arkts.AnnotationUsage | undefined { - let hasTypeDecorator = annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.TYPE - ); - return hasTypeDecorator; -} - -function hasDecoratorType( - node: arkts.ClassDeclaration, -): arkts.AnnotationUsage | undefined { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member) && member.annotations) { - hasTypeDecorator = findTypeDecorator(member.annotations); - } - }); - return hasTypeDecorator; -} - -// rule1: @Type can only be used for class -function checkTypeInStruct( - node: arkts.StructDeclaration, - context: UISyntaxRuleContext, -): void { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member) && member.annotations) { - hasTypeDecorator = findTypeDecorator(member.annotations); - reportInvalidTypeUsageInStruct(hasTypeDecorator, context); - } - }); -} - -function reportInvalidTypeUsageInStruct( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidType, - fix: (hasTypeDecorator) => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -// rule2: Conflict between @Type and @Observed -function checkObservedAndTypeConflict( - node: arkts.ClassDeclaration, - context: UISyntaxRuleContext -): void { - let hasTypeDecorator: arkts.AnnotationUsage | undefined; - node.definition?.annotations.forEach(member => { - const annotation = getAnnotationName(member); - hasTypeDecorator = hasDecoratorType(node); - if (annotation === PresetDecorators.OBSERVED_V1) { - reportObservedAndTypeDecoratorConflict(hasTypeDecorator, context); - } - }); -} - -function reportObservedAndTypeDecoratorConflict( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidDecoratorWith, - fix: () => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -// rule3: @TypeCannot be used for function members -function validateScriptFunctionForTypeDecorator( - node: arkts.ScriptFunction, - context: UISyntaxRuleContext -): void { - const hasTypeDecorator = findTypeDecorator(node.annotations); - reportInvalidTypeDecorator(hasTypeDecorator, context); -} - -function reportInvalidTypeDecorator( - hasTypeDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if (!hasTypeDecorator) { - return; - } - context.report({ - node: hasTypeDecorator, - message: rule.messages.invalidTypeMember, - fix: (hasTypeDecorator) => { - const startPosition = arkts.getStartPosition(hasTypeDecorator); - const endPosition = arkts.getEndPosition(hasTypeDecorator); - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); -} - -const rule: UISyntaxRule = { - name: 'type-decorator-check', - messages: { - invalidType: `The @Type decorator can only be used in 'class'.`, - invalidDecoratorWith: `The @Type decorator can not be used within a 'class' decorated with @Observed.`, - invalidTypeMember: `The @Type can decorate only member variables in a 'class'.` - }, - setup(context) { - return { - parsed: (node): void => { - // Check the decorator on the class - if (arkts.isClassDeclaration(node)) { - checkObservedAndTypeConflict(node, context); - } - if (arkts.isStructDeclaration(node)) { - checkTypeInStruct(node, context); - } - if (arkts.isScriptFunction(node) && node.annotations) { - validateScriptFunctionForTypeDecorator(node, context); - } - }, - }; - }, -}; -export default rule; diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..695b3579f2d12f0340882ca4a70cd17e71ca2989 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts @@ -0,0 +1,275 @@ +/* + * Copyright (c) 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getConsistentResourceInfo } from '../utils'; + +class UiConsistentCheckRule extends AbstractUISyntaxRule { + // UI consistency is only detect in the limited decorator + private static readonly specificDecorators = ['Builder', 'Extend', 'AnimatableExtend']; + // The attributes of the VP unit must be used + private static readonly checkVpProperties = ['padding']; + // The property of VP/PX units must be used + private static readonly checkVpAndPxProperties = [ + 'borderWidth', 'borderRadius', 'outlineWidth', 'outlineRadius' + ]; + // The types of colors allowed + private static readonly colorUnionTypes = ['Color', 'Resource', 'string']; + // Resource color type tags + private static readonly resourceColorType = 'ResourceColor'; + + private static readonly consistentResourceInfo = getConsistentResourceInfo(); + + public setup(): Record { + return { + colorConsistentWarning: 'It is recommended that you use layered parameters for easier color mode switching and theme color changing.', + vpSizeWarning: 'It is recommended that you use layered parameters for polymorphism development and resolution adaptation.', + vpAndPxSizeWarning: 'It is recommended that you use layered parameters for polymorphism development and resolution adaptation.', + }; + } + + public parsed(node: arkts.StructDeclaration): void { + // Specific Attributes: Check the VP units + this.checkVpUnit(node); + // Specific attributes: Check the VP and PX units + this.checkVpAndPxUnit(node); + // Check the color parameter formatting + this.checkColorParams(node); + } + + private isHexColor(color: string): boolean { + return /^(#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8}))|(0x[0-9A-F]*)$/i.test( + color, + ); + } + + private isValidPx(size: string): boolean { + return /^\d+(\.\d+)?px$/.test(size); + } + + private isValidVp(size: string): boolean { + return /^\d+(\.\d+)?vp$/.test(size); + } + + private convertToDecimalPx(size: string): string { + // Remove meaningless 0, eg: '12.00px' + const formatSize = `${parseFloat(size)}${size.endsWith('px') ? 'px' : 'vp' + }`; + // Regular expressions match numbers and units (e.g. px) + const regex = /^(\d+)(px|vp)$/; + const match = regex.exec(formatSize); + + if (match) { + // Get the numerical part and the unit part + const numberPart = match[1]; + const unitPart = match[2]; + + // Add a decimal point and a zero if there is no decimal point after the original number + const decimalSize = `${parseFloat(numberPart).toFixed(1)}${unitPart}`; + + return decimalSize; + } else { + // If there is no match, the original string is returned + return size; + } + } + + // Check whether it is in the UI component + private isInUIComponent(node: arkts.AstNode): boolean | undefined { + if (!node) { + return false; + } + let curNode = node; + try { + while (!arkts.isStructDeclaration(curNode)) { + if (!curNode.parent) { + return false; + } + curNode = curNode.parent; + } + return true; + } catch (error) { + return false; + } + } + + // Whether or not it is decorated by a specific decorator + private isInSpecificDecorators(node: arkts.AstNode): boolean { + if (!node) { + return false; + } + let curNode = node; + try { + while (!arkts.isFunctionDeclaration(curNode)) { + if (!curNode.parent) { + return false; + } + curNode = curNode.parent; + } + const annotations = arkts.getAnnotations(curNode); + return annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name && UiConsistentCheckRule.specificDecorators.includes(annotation.expr.name) + ); + } catch (error) { + return false; + } + } + + private isColorProperty(propertyName: string): boolean { + // If the attribute name contains 'ResourceColor', 'Color', 'Resource', 'string', it is maybe a color property + return propertyName.includes(UiConsistentCheckRule.resourceColorType) || + UiConsistentCheckRule.colorUnionTypes.some(str => propertyName.includes(str)); + } + + private checkVpUnit(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose unit is VP + if (!arkts.isIdentifier(node.expression.property) || + !UiConsistentCheckRule.checkVpProperties.includes(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const sizeParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && this.isValidVp(argNode.str) + ); + sizeParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get( + this.convertToDecimalPx((argNode as arkts.StringLiteral).str) + ); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.vpSizeWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } + + private checkVpAndPxUnit(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose unit is VP or PX + if (!arkts.isIdentifier(node.expression.property) || + !UiConsistentCheckRule.checkVpAndPxProperties.includes(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const sizeParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && (this.isValidVp(argNode.str) || this.isValidPx(argNode.str)) + ); + sizeParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get( + this.convertToDecimalPx((argNode as arkts.StringLiteral).str) + ); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.vpAndPxSizeWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } + + private checkColorParams(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression || + !arkts.isMemberExpression(node.expression) || !node.expression.property) { + return; + } + // Verify the attribute whose type is Color + if (!arkts.isIdentifier(node.expression.property) || + !this.isColorProperty(node.expression.property.name)) { + return; + } + // Only the content under the UI component and the special decorator is verified + if (!this.isInUIComponent(node.expression.property) && + !this.isInSpecificDecorators(node.expression.property)) { + return; + } + // Gets the attribute value text and verifies the formatting + const colorParams = node.arguments.filter( + argNode => arkts.isStringLiteral(argNode) && this.isHexColor(argNode.str) + ); + colorParams.forEach(argNode => { + const resources = + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str) ?? + UiConsistentCheckRule.consistentResourceInfo.get((argNode as arkts.StringLiteral).str.toUpperCase()); + + // If consistent resource information doesn't exist, it won't be fixed + if (!resources || resources.length < 1) { + return; + } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.color.${resources[0].resourceName}')`; + this.report({ + node: argNode, + message: this.messages.colorConsistentWarning, + fix: () => { + return { + title: `change ${propertyValue} to ${targetValue}`, + range: [argNode.startPosition, argNode.endPosition], + code: `${targetValue}`, + }; + } + }); + }); + } +} + +export default UiConsistentCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts index c71a7c298ca697f0d2de52c0645c5e56f0923a8f..817d5ddd00dca49aee2bf960c81d34f42744d5cd 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -14,30 +14,75 @@ */ import * as arkts from '@koalaui/libarkts'; +import { ProjectConfig } from 'common/plugin-context'; +import { UISyntaxRuleComponents } from 'ui-syntax-plugins/utils'; export type FixSuggestion = { - range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; - code: string; + range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; + title?: string; + code: string; }; export type ReportOptions = { - node: arkts.AstNode; - message: string; - data?: Record; - fix?: (node: arkts.AstNode) => FixSuggestion; + node: arkts.AstNode; + message: string; + data?: Record; + fix?: (node: arkts.AstNode) => FixSuggestion; + level?: UISyntaxRuleLevel; }; export type UISyntaxRuleContext = { - report(options: ReportOptions): void; - containerComponents?: Set | undefined; + projectConfig?: ProjectConfig; + componentsInfo: UISyntaxRuleComponents | undefined; + report(options: ReportOptions): void; + getMainPages(): string[]; }; -export type UISyntaxRuleHandler = (node: arkts.AstNode) => void; +export type UISyntaxRulePhaseHandler = (node: arkts.AstNode) => void; + +export type UISyntaxRuleHandler = { + parsed?: UISyntaxRulePhaseHandler; + checked?: UISyntaxRulePhaseHandler; +}; export type UISyntaxRule = { - name: string; - messages: Record; - setup(context: UISyntaxRuleContext): { - parsed?: UISyntaxRuleHandler; - }; + name: string; + messages: Record; + setup(context: UISyntaxRuleContext): UISyntaxRuleHandler; +}; + +export type UISyntaxRuleReportOptions = { + node: arkts.AstNode; + message: string; + data?: Record; + fix?: (node: arkts.AstNode) => FixSuggestion; }; + +export type UISyntaxRuleLevel = 'error' | 'warn' | 'none'; + +export interface UISyntaxRuleConstructor { + new(context: UISyntaxRuleContext, level: UISyntaxRuleLevel): AbstractUISyntaxRule; +} + +export abstract class AbstractUISyntaxRule { + protected messages: Record; + + constructor(protected context: UISyntaxRuleContext, protected level: UISyntaxRuleLevel) { + this.messages = this.setup(); + } + + public beforeTransform(): void { } + public afterTransform(): void { } + public parsed(node: arkts.AstNode): void { } + public checked(node: arkts.AstNode): void { } + public abstract setup(): Record; + + protected report(options: UISyntaxRuleReportOptions): void { + this.context.report({ + ...options, + level: this.level, + }); + } +} + +export type UISyntaxRuleConfig = [UISyntaxRuleConstructor, UISyntaxRuleLevel]; diff --git a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts index 938c3a210931105d572ea62ab5011d5b68b281b3..d187edd5257846eacf9b930e6d8a729fe78ec784 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts @@ -14,122 +14,153 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const NOT_PARAM_LENGTH: number = 0; -const BUILD_NAME: string = 'build'; const BUILD_FUNCTION_COUNT_INI: number = 0; const BUILD_FUNCTION_COUNT: number = 1; const NOT_STATEMENT_LENGTH: number = 0; -// rule1: Check if the build function contains arguments and report an error -function validateBuildFunctionParameters(buildFunction: arkts.MethodDefinition, context: UISyntaxRuleContext): void { - const paramsNodes = buildFunction.scriptFunction.params; - if (paramsNodes.length > NOT_PARAM_LENGTH) { - paramsNodes.forEach((param) => { - if (arkts.isEtsParameterExpression(param)) { - reportBuildParamNotAllowed(param, context); - } - }); - } -} +class ValidateBuildInStructRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidComponent: `The 'build' method can not have arguments.`, + invalidBuild: `The struct '{{structName}}' must have at least and at most one 'build' method.`, + }; + } -// Report an error with an unallowed parameter in the build function -function reportBuildParamNotAllowed( - param: arkts.ETSParameterExpression, - context: UISyntaxRuleContext -): void { - context.report({ - node: param, - message: rule.messages.invalidComponet, - fix: (param) => { - const startPosition = arkts.getStartPosition(param); - const endPosition = arkts.getEndPosition(param); - return { - range: [startPosition, endPosition], - code: '' - }; + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; + this.validateBuild(node, buildFunctionCount); } - }); -} -function validateConstructorForBuildFunction( - node: arkts.StructDeclaration, - member: arkts.MethodDefinition, - buildFunctionCount: number, - context: UISyntaxRuleContext -): void { - const blockStatement = member.scriptFunction.body; - if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { - return; - } - const statements = blockStatement.statements; - const structName = node.definition.ident; - if (buildFunctionCount !== BUILD_FUNCTION_COUNT && - statements.length === NOT_STATEMENT_LENGTH) { - reportMissingBuildInStruct(structName, blockStatement, context); - } -} + private validateBuild( + node: arkts.StructDeclaration, + buildFunctionCount: number + ): void { + node.definition.body.forEach((member) => { + // Check if the member is defined for the method and the method name is 'build' + if (arkts.isMethodDefinition(member) && arkts.isIdentifier(member.name) && getIdentifierName(member.name) === BUILD_NAME) { + buildFunctionCount++; + this.validateBuildFunctionParameters(member); + this.validateDuplicateBuild(buildFunctionCount, member); + } + // rule2: This rule validates the use of the 'build' function + if (arkts.isMethodDefinition(member) && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR === member.kind) { + this.validateConstructorForBuildFunction(node, member, buildFunctionCount); + } + }); + } -function reportMissingBuildInStruct( - structName: arkts.Identifier | undefined, - blockStatement: arkts.BlockStatement, - context: UISyntaxRuleContext -): void { - if (!structName) { - return; - } - context.report({ - node: structName, - message: rule.messages.invalidBuild, - fix: (structName) => { - const startPosition = arkts.getStartPosition(blockStatement); - const endPosition = startPosition; - return { - range: [startPosition, endPosition], - code: '{\nbuild {\n}' - }; + // rule1: Check if the build function contains arguments and report an error + private validateBuildFunctionParameters(buildFunction: arkts.MethodDefinition): void { + const paramsNodes = buildFunction.scriptFunction.params; + if (paramsNodes.length > NOT_PARAM_LENGTH) { + paramsNodes.forEach((param) => { + if (arkts.isEtsParameterExpression(param)) { + this.reportBuildParamNotAllowed(param); + } + }); + } } - }); -} -function validateBuild( - node: arkts.StructDeclaration, - buildFunctionCount: number, - context: UISyntaxRuleContext, -): void { - node.definition.body.forEach((member) => { - // Check if the member is defined for the method and the method name is 'build' - if (arkts.isMethodDefinition(member) && getIdentifierName(member.name) === BUILD_NAME) { - buildFunctionCount++; - validateBuildFunctionParameters(member, context); + private validateDuplicateBuild( + buildFunctionCount: number, + member: arkts.MethodDefinition, + ): void { + if (buildFunctionCount > BUILD_FUNCTION_COUNT) { + const buildNode = member.scriptFunction.id; + if (!buildNode) { + return; + } + if (!arkts.isIdentifier(buildNode)) { + return; + } + this.report({ + node: member, + message: this.messages.invalidBuild, + data: { + structName: getIdentifierName(buildNode), + }, + fix: () => { + const startPosition = member.startPosition; + const endPosition = member.endPosition; + return { + title: 'Remove the duplicate build function.', + range: [startPosition, endPosition], + code: `` + }; + } + }); + } } - // rule2: This rule validates the use of the 'build' function - if (arkts.isMethodDefinition(member) && - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR === member.kind) { - validateConstructorForBuildFunction(node, member, buildFunctionCount, context); + + private validateConstructorForBuildFunction( + node: arkts.StructDeclaration, + member: arkts.MethodDefinition, + buildFunctionCount: number, + ): void { + const blockStatement = member.scriptFunction.body; + if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { + return; + } + const statements = blockStatement.statements; + const structName = node.definition.ident; + if (buildFunctionCount === BUILD_FUNCTION_COUNT_INI && + statements.length === NOT_STATEMENT_LENGTH) { + this.reportMissingBuildInStruct(structName, node); + } } - }); -} -const rule: UISyntaxRule = { - name: 'validate-build-in-struct', - messages: { - invalidComponet: `A custom component can have only one 'build' function, which does not require parameters.`, - invalidBuild: `This rule validates the use of the 'build' function`, - }, - setup(context) { - return { - parsed: (node: arkts.AstNode): void => { - if (!arkts.isStructDeclaration(node)) { - return; + // Report an error with an unallowed parameter in the build function + private reportBuildParamNotAllowed( + param: arkts.ETSParameterExpression, + ): void { + this.report({ + node: param, + message: this.messages.invalidComponent, + fix: (param) => { + const startPosition = param.startPosition; + const endPosition = param.endPosition; + return { + title: 'Remove the parmeters of the build function', + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportMissingBuildInStruct( + structName: arkts.Identifier | undefined, + node: arkts.StructDeclaration, + ): void { + if (!structName) { + return; } - let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; - validateBuild(node, buildFunctionCount, context); - }, - }; - }, -}; + this.report({ + node: structName, + message: this.messages.invalidBuild, + data: { + structName: getIdentifierName(structName), + }, + fix: () => { + let startPosition = node.endPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = startPosition; + return { + title: 'Add a build function to the custom component', + range: [startPosition, endPosition], + code: 'build() {\n}\n' + }; + } + }); + } +} -export default rule; \ No newline at end of file +export default ValidateBuildInStructRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts b/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts new file mode 100644 index 0000000000000000000000000000000000000000..d48cb7dd8122e6c012e0f5663f35ffc53e0eaf1b --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts @@ -0,0 +1,123 @@ +/* + * Copyright (c) 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 { PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +// Can only be used with decorators for struct +const structOnlyDecorators = [ + PresetDecorators.REUSABLE_V1, + PresetDecorators.REUSABLE_V2, + PresetDecorators.COMPONENT_V1, + PresetDecorators.COMPONENT_V2, + PresetDecorators.ENTRY, + PresetDecorators.PREVIEW, + PresetDecorators.CUSTOM_DIALOG, +]; + +// Can only be used with decorators for property +const propertyOnlyDecorators = [ + PresetDecorators.STATE, + PresetDecorators.PROP_REF, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP_REF, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.WATCH, + PresetDecorators.REQUIRE, + PresetDecorators.OBJECT_LINK, +]; + +class ValidateDecoratorTargetRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + decoratorOnlyWithStruct: `The '@{{decoratorName}}' annotation can only be used with 'struct'.`, + decoratorOnlyWithMemberProperty: `'@{{decoratorName}}' can only decorate member property.` + }; + } + + public parsed(node: arkts.AstNode): void { + this.validateDecoratorPropertyOnly(node); + + if (!arkts.isStructDeclaration(node)) { + this.validateDecoratorStructOnly(node); + } + } + + private validateDecoratorPropertyOnly( + node: arkts.AstNode, + ): void { + if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + node.annotations.forEach((annotation) => { + this.validateDecorator(annotation, propertyOnlyDecorators, this.messages.decoratorOnlyWithMemberProperty); + }); + } + } + + private validateDecoratorStructOnly(node: arkts.AstNode): void { + // class + if (arkts.isClassDeclaration(node)) { + node.definition?.annotations?.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + // function/ variable/ method/ classproperty/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isClassProperty(node) || + arkts.isScriptFunction(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + node.annotations.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + + // get /set method + if (arkts.isMethodDefinition(node) && + (node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET || + node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET)) { + node.scriptFunction.annotations.forEach((annotation) => { + this.validateDecorator(annotation, structOnlyDecorators, this.messages.decoratorOnlyWithStruct); + }); + } + } + + // decorator check function + private validateDecorator( + annotation: arkts.AnnotationUsage, + decorator: string[], + message: string, + ): void { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + if (decorator.includes(annotation.expr.name)) { + this.report({ + node: annotation, + message: message, + data: { + decoratorName: annotation.expr.name, + }, + }); + } + } + } +} + +export default ValidateDecoratorTargetRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts deleted file mode 100644 index 7660e662468b62dd664167a283982c3b6a6b3a73..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-cons.ts +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 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 { getIdentifierName, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; - -// Define a function to add property data to the property map -function addProperty(propertyMap: Map>, structName: string, - propertyName: string, annotationName: string): void { - if (!propertyMap.has(structName)) { - propertyMap.set(structName, new Map()); - } - const structProperties = propertyMap.get(structName); - if (structProperties) { - structProperties.set(propertyName, annotationName); - } -} -// categorizePropertyBasedOnAnnotations -function checkPropertyByAnnotations( - item: arkts.AstNode, - structName: string, - mustInitMap: Map>, - cannotInitMap: Map> = new Map(), - mustInitArray: string[][], - cannotInitArray: string[][] -): void { - if (!arkts.isClassProperty(item)) { - return; - } - const propertyName: string = item.key?.dumpSrc() ?? ''; - if (item.annotations.length === 0 || propertyName === '') { - return; - } - const annotationArray: string[] = getClassPropertyAnnotationNames(item); - // If the member variable is decorated, it is added to the corresponding map - mustInitArray.forEach(arr => { - if (arr.every(annotation => annotationArray.includes(annotation))) { - const annotationName: string = arr[0]; - addProperty(mustInitMap, structName, propertyName, annotationName); - } - }); - cannotInitArray.forEach(arr => { - if (arr.every(annotation => annotationArray.includes(annotation))) { - const annotationName: string = arr[0]; - addProperty(cannotInitMap, structName, propertyName, annotationName); - } - }); -} - -function initMap( - node: arkts.AstNode, - mustInitMap: Map>, - cannotInitMap: Map> = new Map(), - mustInitArray: string[][], - cannotInitArray: string[][] -): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member)) { - return; - } - const structName: string = member.definition.ident?.name ?? ''; - if (structName === '') { - return; - } - member.definition?.body.forEach((item) => { - checkPropertyByAnnotations(item, structName, mustInitMap, cannotInitMap, mustInitArray, cannotInitArray); - }); - }); -} - -function getChildKeyNameArray(member: arkts.AstNode): string[] { - const childkeyNameArray: string[] = []; - member.getChildren().forEach((property) => { - if (arkts.isProperty(property)) { - const childkeyName = property.key?.dumpSrc() ?? ''; - if (childkeyName !== '') { - childkeyNameArray.push(childkeyName); - } - } - }); - return childkeyNameArray; -} - -function checkMustInitialize( - node: arkts.AstNode, - context: UISyntaxRuleContext, - mustInitMap: Map> -): void { - if (!arkts.isIdentifier(node)) { - return; - } - const structName: string = getIdentifierName(node); - if (!mustInitMap.has(structName)) { - return; - } - const parentNode: arkts.AstNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // Get all the properties of a record via StructName - const mustInitName: Map = mustInitMap.get(structName)!; - parentNode.arguments?.forEach((member) => { - const childkeyNameArray: string[] = getChildKeyNameArray(member); - mustInitName.forEach((value, key) => { - // If an attribute that must be initialized is not initialized, an error is reported - if (!childkeyNameArray.includes(key)) { - context.report({ - node: parentNode, - message: rule.messages.mustInitializeRule, - data: { - annotationName: value, - propertyName: key, - }, - }); - } - }); - }); -} - -function checkCannotInitialize( - node: arkts.AstNode, - context: UISyntaxRuleContext, - cannotInitMap: Map> -): void { - if (!arkts.isIdentifier(node)) { - return; - } - const structName: string = getIdentifierName(node); - if (!cannotInitMap.has(structName)) { - return; - } - const parentNode: arkts.AstNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - } - // Get all the properties of a record via StructName - const cannotInitName: Map = cannotInitMap.get(structName)!; - parentNode.arguments.forEach((member) => { - member.getChildren().forEach((property) => { - if (!arkts.isProperty(property)) { - return; - } - if (!property.key) { - return; - } - const propertyName = property.key.dumpSrc(); - // If a property that cannot be initialized is initialized, an error is reported - if (cannotInitName.has(propertyName)) { - context.report({ - node: property.key, - message: rule.messages.cannotInitializeRule, - data: { - annotationName: cannotInitName.get(propertyName)!, - propertyName: propertyName, - }, - }); - } - }); - }); -} - -const rule: UISyntaxRule = { - name: 'variable-initialization-via-component-cons', - messages: { - mustInitializeRule: `'@{{annotationName}}' decorated '{{propertyName}}' must be initialized through the component constructor.`, - cannotInitializeRule: `'@{{annotationName}}' decorated '{{propertyName}}' cannot be initialized through the component constructor.`, - }, - setup(context) { - let mustInitMap: Map> = new Map(); - let cannotInitMap: Map> = new Map(); - const mustInitArray: string[][] = [ - [PresetDecorators.REQUIRE, PresetDecorators.PROP], - [PresetDecorators.REQUIRE, PresetDecorators.BUILDER_PARAM], - [PresetDecorators.LINK], - [PresetDecorators.OBJECT_LINK] - ]; - const cannotInitArray: string[][] = [ - [PresetDecorators.STORAGE_LINK], - [PresetDecorators.STORAGE_PROP], - [PresetDecorators.CONSUME], - [PresetDecorators.LOCAL_STORAGE_LINK], - [PresetDecorators.LOCAL_STORAGE_PROP] - ]; - return { - parsed: (node): void => { - initMap(node, mustInitMap, cannotInitMap, mustInitArray, cannotInitArray); - checkMustInitialize(node, context, mustInitMap); - checkCannotInitialize(node, context, cannotInitMap); - }, - }; - }, -}; - -export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts new file mode 100644 index 0000000000000000000000000000000000000000..29c7209c60e4cd87849d9c913e9831d1361e375e --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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 { getIdentifierName, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class VariableInitializationViaComponentConstructorRule extends AbstractUISyntaxRule { + private mustInitMap: Map> = new Map(); + private cannotInitMap: Map> = new Map(); + + private static readonly mustInitInConstructorDecorators: string[][] = [ + [PresetDecorators.REQUIRE], + [PresetDecorators.REQUIRE, PresetDecorators.STATE], + [PresetDecorators.REQUIRE, PresetDecorators.PROVIDE], + [PresetDecorators.REQUIRE, PresetDecorators.PROP_REF], + [PresetDecorators.REQUIRE, PresetDecorators.BUILDER_PARAM], + [PresetDecorators.REQUIRE, PresetDecorators.PARAM] + ]; + + private static readonly notAllowInitInConstructorDecorators: string[][] = [ + [PresetDecorators.STORAGE_LINK], + [PresetDecorators.STORAGE_PROP_REF], + [PresetDecorators.CONSUME], + [PresetDecorators.LOCAL_STORAGE_LINK], + ]; + + public setup(): Record { + return { + requireVariableInitializationViaComponentConstructor: `'@Require' decorated '{{varName}}' must be initialized through the component constructor.`, + disallowVariableInitializationViaComponentConstructor: `The '{{decoratorName}}' property '{{varName}}' in the custom component '{{customComponentName}}' cannot be initialized here (forbidden to specify).`, + }; + } + + public beforeTransform(): void { + this.mustInitMap = new Map(); + this.cannotInitMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + this.initMap(node); + this.checkMustInitialize(node); + this.checkCannotInitialize(node); + } + + // Define a function to add property data to the property map + private addProperty( + propertyMap: Map>, + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); + } + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } + } + // categorizePropertyBasedOnAnnotations + private checkPropertyByAnnotations(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item) || !item.key || !arkts.isIdentifier(item.key)) { + return; + } + const propertyName: string = item.key.name; + if (item.annotations.length === 0 || propertyName === '') { + return; + } + const annotationArray: string[] = getClassPropertyAnnotationNames(item); + // If the member variable is decorated, it is added to the corresponding map + VariableInitializationViaComponentConstructorRule.mustInitInConstructorDecorators.forEach(arr => { + if (arr.every(annotation => annotationArray.includes(annotation))) { + const annotationName: string = arr[0]; + this.addProperty(this.mustInitMap, structName, propertyName, annotationName); + } + }); + VariableInitializationViaComponentConstructorRule.notAllowInitInConstructorDecorators.forEach(arr => { + if (arr.every(annotation => annotationArray.includes(annotation))) { + const annotationName: string = arr[0]; + this.addProperty(this.cannotInitMap, structName, propertyName, annotationName); + } + }); + } + + private initMap(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member)) { + return; + } + if (!member.definition || !member.definition.ident || !arkts.isIdentifier(member.definition.ident)) { + return; + } + const structName: string = member.definition.ident.name; + if (structName === '') { + return; + } + member.definition?.body.forEach((item) => { + this.checkPropertyByAnnotations(item, structName); + }); + }); + } + + private getChildKeyNameArray(node: arkts.CallExpression): string[] { + const childKeyNameArray: string[] = []; + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property)) { + return; + } + if (!property.key || !arkts.isIdentifier(property.key)) { + return; + } + const childKeyName = property.key.name; + if (childKeyName !== '') { + childKeyNameArray.push(childKeyName); + } + }); + }); + return childKeyNameArray; + } + + private checkMustInitialize(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + if (!arkts.isIdentifier(node.expression)) { + return; + } + const structName: string = getIdentifierName(node.expression); + if (!this.mustInitMap.has(structName)) { + return; + } + // Get all the properties of a record via StructName + const mustInitProperty: Map = this.mustInitMap.get(structName)!; + const childKeyNameArray: string[] = this.getChildKeyNameArray(node); + // If an attribute that must be initialized is not initialized, an error is reported + mustInitProperty.forEach((value, key) => { + if (!childKeyNameArray.includes(key)) { + this.report({ + node: node, + message: this.messages.requireVariableInitializationViaComponentConstructor, + data: { + varName: key, + }, + }); + } + }); + } + + private checkCannotInitialize(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + if (!arkts.isIdentifier(node.expression)) { + return; + } + const structName: string = getIdentifierName(node.expression); + if (!this.cannotInitMap.has(structName)) { + return; + } + // Get all the properties of a record via StructName + const cannotInitName: Map = this.cannotInitMap.get(structName)!; + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property)) { + return; + } + if (!property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName = property.key.name; + // If a property that cannot be initialized is initialized, an error is reported + if (cannotInitName.has(propertyName)) { + const propertyType: string = cannotInitName.get(propertyName)!; + this.report({ + node: property, + message: this.messages.disallowVariableInitializationViaComponentConstructor, + data: { + decoratorName: `@${propertyType}`, + varName: propertyName, + customComponentName: structName + }, + }); + } + }); + }); + } +} + +export default VariableInitializationViaComponentConstructorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts index dae346a7958033a1d2c0dc703d2ece3c2554682c..fe2135a6e9a5c4ec74509f4270a6cb382897bb0b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts @@ -14,115 +14,181 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, isPrivateClassProperty, PresetDecorators, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -// Gets the names of all methods in the struct -function getMethodNames(node: arkts.StructDeclaration): string[] { - const methodNames: string[] = []; - node.definition.body.forEach((member) => { - if (arkts.isMethodDefinition(member)) { - const methodName = getIdentifierName(member.name); - if (methodName) { - methodNames.push(methodName); - } +class WatchDecoratorFunctionRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidWatch: `'@watch' cannot be used with '{{parameterName}}'. Apply it only to parameters that correspond to existing methods.`, + stringOnly: `'@Watch' cannot be used with '{{parameterName}}'. Apply it only to 'string' parameters.`, + }; } - }); - return methodNames; -} -// Invalid @Watch decorator bugs are reported -function reportInvalidWatch( - member: arkts.ClassProperty, - methodName: string, - hasWatchDecorator: arkts.AnnotationUsage, - context: UISyntaxRuleContext -): void { - context.report({ - node: hasWatchDecorator, - message: rule.messages.invalidWatch, - data: { methodName }, - fix: () => { - const startPosition = arkts.getEndPosition(member); - const endPosition = arkts.getEndPosition(member); - return { - range: [startPosition, endPosition], - code: `\n${methodName}(){\n}`, - }; - }, - }); -} + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + // Get all method names + const methodNames = this.getMethodNames(node); + // Get a private variable + const privateNames = this.getPrivateNames(node); + this.validateWatch(node, methodNames, privateNames); + } -function validateWatchDecorator( - member: arkts.ClassProperty, - methodNames: string[], - hasWatchDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - member.annotations.forEach((annotation) => { - validateWatchProperty(annotation, member, methodNames, hasWatchDecorator, context); - }); -} + private getExpressionValue(parameters: arkts.Expression, privateNames: string[]): string { + const type = arkts.nodeType(parameters); + if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL) { + return parameters.dumpSrc(); // Try extracting the string representation with dumpSrc + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL) { + return parameters.dumpSrc(); + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL) { + return 'null'; + } else if (type === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL) { + return 'undefined'; + } else if (arkts.isMemberExpression(parameters)) { + if (arkts.isIdentifier(parameters.property)) { + if (privateNames.includes(parameters.property.name)) { + return parameters.property.name; + } + } + } + return parameters.dumpSrc(); // By default, an empty string is returned + } -function validateWatchProperty( - annotation: arkts.AnnotationUsage, - member: arkts.ClassProperty, - methodNames: string[], - hasWatchDecorator: arkts.AnnotationUsage | undefined, - context: UISyntaxRuleContext -): void { - if ( - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ) { - annotation.properties.forEach((element) => { - if (!arkts.isClassProperty(element)) { - return; - } - const methodName = element.value?.dumpSrc().slice(1, -1); - if (hasWatchDecorator && methodName && !methodNames.includes(methodName)) { - reportInvalidWatch(member, methodName, hasWatchDecorator, context); - } - }); - } + // Gets the names of all methods in the struct + private getMethodNames(node: arkts.StructDeclaration): string[] { + const methodNames: string[] = []; + node.definition.body.forEach((member) => { + if (arkts.isMethodDefinition(member) && arkts.isIdentifier(member.name)) { + const methodName = getIdentifierName(member.name); + if (methodName) { + methodNames.push(methodName); + } + } + }); + return methodNames; + } -} + private getPrivateNames(node: arkts.StructDeclaration): string[] { + const privateNames: string[] = []; + node.definition.body.forEach((member) => { + if (arkts.isClassProperty(member) && isPrivateClassProperty(member)) { + const privateName = getClassPropertyName(member); + if (privateName) { + privateNames.push(privateName); + } + } + }); + return privateNames; + } -function validateWatch( - node: arkts.StructDeclaration, - methodNames: string[], - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + private validateWatch( + node: arkts.StructDeclaration, + methodNames: string[], + privateNames: string[] + ): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const watchDecorator = findDecorator(member, PresetDecorators.WATCH); + // Determine whether it contains @watch decorators + this.validateWatchDecorator(member, methodNames, privateNames, watchDecorator); + }); } - const hasWatchDecorator = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ); - // Determine whether it contains @watch decorators - validateWatchDecorator(member, methodNames, hasWatchDecorator, context); - }); -} -const rule: UISyntaxRule = { - name: 'watch-decorator-function', - messages: { - invalidWatch: `The '@Watch' decorated parameter must be a callback '{{methodName}}' of a function in a custom component.`, - }, - setup(context) { - return { - parsed: (node): void => { - if (!arkts.isStructDeclaration(node)) { - return; + private validateWatchDecorator( + member: arkts.ClassProperty, + methodNames: string[], + privateNames: string[], + watchDecorator: arkts.AnnotationUsage | undefined + ): void { + member.annotations.forEach((annotation) => { + this.validateWatchProperty(annotation, member, methodNames, privateNames, watchDecorator); + }); + } + + private validateWatchProperty( + annotation: arkts.AnnotationUsage, + member: arkts.ClassProperty, + methodNames: string[], + privateNames: string[], + watchDecorator: arkts.AnnotationUsage | undefined + ): void { + if ( + !annotation.expr || + !arkts.isIdentifier(annotation.expr) || + annotation.expr.name !== PresetDecorators.WATCH + ) { + return; } - // Get all method names - const methodNames = getMethodNames(node); - validateWatch(node, methodNames, context); - }, - }; - }, -}; + annotation.properties.forEach((element) => { + if (!arkts.isClassProperty(element)) { + return; + } + if (!element.value) { + return; + } + if (!arkts.isStringLiteral(element.value)) { + if (!watchDecorator) { + return; + } + this.reportStringOnly(element.value, privateNames, watchDecorator); + return; + } + const parameterName = element.value.str; + if (watchDecorator && parameterName && !methodNames.includes(parameterName)) { + this.reportInvalidWatch(member, parameterName, watchDecorator); + } + }); + } + + // Invalid @Watch decorator bugs are reported + private reportInvalidWatch( + member: arkts.ClassProperty, + parameterName: string, + watchDecorator: arkts.AnnotationUsage + ): void { + this.report({ + node: watchDecorator, + message: this.messages.invalidWatch, + data: { parameterName }, + fix: () => { + const startPosition = member.endPosition; + const endPosition = member.endPosition; + return { + title: 'Add a watch function to the custom component', + range: [startPosition, endPosition], + code: `\n${parameterName}(){\n}`, + }; + }, + }); + } + + private reportStringOnly( + parameters: arkts.Expression | undefined, + privateNames: string[], + watchDecorator: arkts.AnnotationUsage + ): void { + if (!parameters) { + return; + } + this.report({ + node: watchDecorator, + message: this.messages.stringOnly, + data: { parameterName: this.getExpressionValue(parameters, privateNames) }, + fix: () => { + const startPosition = parameters.startPosition; + const endPosition = parameters.endPosition; + return { + title: 'Remove the parameters', + range: [startPosition, endPosition], + code: ``, + }; + }, + }); + } +} -export default rule; \ No newline at end of file +export default WatchDecoratorFunctionRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts index 22eb47c74686b3882d25c52357c8e1079e7685e1..fc2c30f3999f355d761f7a7d37193249fad67eab 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts @@ -14,47 +14,46 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { findDecorator, getClassPropertyAnnotationNames, getClassPropertyName, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; const PROPERTY_ANNOTATION_NUM: number = 2; -function validateWatchDecorator(node: arkts.StructDeclaration, context: UISyntaxRuleContext): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; +class WatchDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidWatch: `Regular variable '{{propertyName}}' can not be decorated with '@Watch'.`, + }; } - const hasWatchDecorator = member.annotations?.find(annotation => - annotation.expr && - annotation.expr.dumpSrc() === PresetDecorators.WATCH - ); - const propertyAnnotationNames = getClassPropertyAnnotationNames(member); - // Determine if there are any decorations other than @watch decorations - // rule1: The @Watch decorator must be used with other decorators - if (hasWatchDecorator && propertyAnnotationNames.length < PROPERTY_ANNOTATION_NUM) { - context.report({ - node: hasWatchDecorator, - message: rule.messages.invalidWatch, - }); - } - }); -} -const rule: UISyntaxRule = { - name: 'watch-decorator-regular', - messages: { - invalidWatch: `The @Watch decorator must be used with other decorators.`, - }, - setup(context) { - return { - parsed: (node): void => { + public parsed(node: arkts.AstNode): void { if (!arkts.isStructDeclaration(node)) { - return; + return; } - validateWatchDecorator(node, context); - }, - }; - }, -}; + this.validateWatchDecorator(node); + } + + private validateWatchDecorator(node: arkts.StructDeclaration): void { + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const watchDecorator = findDecorator(member, PresetDecorators.WATCH); + const propertyAnnotationNames = getClassPropertyAnnotationNames(member); + const propertyName = getClassPropertyName(member); + // Determine if there are any decorations other than @watch decorations + // rule1: The @Watch decorator must be used with other decorators + if (propertyName && watchDecorator && propertyAnnotationNames.length < PROPERTY_ANNOTATION_NUM) { + this.report({ + node: watchDecorator, + message: this.messages.invalidWatch, + data: { + propertyName: propertyName + } + }); + } + }); + } +} -export default rule; +export default WatchDecoratorRegularRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts b/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts index 5f87ef3fc7ac23eeae4e586a5972049d2a04ce29..a8f2da56f9eebd4c64c0842de3d10a14da6bc8a5 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.ts @@ -14,96 +14,73 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getIdentifierName, getAnnotationName, PresetDecorators } from '../utils'; -import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +import { getIdentifierName, PresetDecorators, WRAP_BUILDER, getFunctionAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const WRAPBUILDER_NAME: string = 'wrapBuilder'; -// Collect all the function names that are decorated with @Builder -function collectBuilderFunctions(node: arkts.EtsScript, builderFunctionNames: string[]): void { - node.statements.forEach((statement) => { - if (!arkts.isFunctionDeclaration(statement)) { - return; +class StructNoExtendsRule extends AbstractUISyntaxRule { + private builderFunctionNames: string[] = []; + + public setup(): Record { + return { + invalidWrapBuilderCheck: 'The wrapBuilder\'s parameter should be @Builder function.', + }; } - const annotations = statement.annotations; - if (!annotations) { - return; + + public beforeTransform(): void { + this.builderFunctionNames = []; } - annotations.forEach((annotation) => { - const decoratorName = getAnnotationName(annotation); - // Find all the functions that are decorated with @Builder and note their names - if (!decoratorName.includes(PresetDecorators.BUILDER)) { - return; - } - const functionName = statement.scriptFunction.id?.name; - if (!functionName || builderFunctionNames.includes(functionName)) { - return; - } - builderFunctionNames.push(functionName); - }); - }); -} -// Verify that the wrapBuilder's arguments are decorated with @Builder -function validateWrapBuilderArguments( - member: arkts.ClassProperty, - context: UISyntaxRuleContext, - builderFunctionNames: string[] -): void { - member.getChildren().forEach((child) => { - if (!arkts.isCallExpression(child) || child.expression.dumpSrc() !== WRAPBUILDER_NAME) { - return; + public parsed(node: arkts.StructDeclaration): void { + this.collectBuilderFunctions(node); + this.validateWrapBuilderInIdentifier(node); } - let functionName: string | undefined; - child.arguments.forEach(firstArgument => { - if (arkts.isMemberExpression(firstArgument)) { - functionName = getIdentifierName(firstArgument.property); - } else if (arkts.isIdentifier(firstArgument)) { - functionName = firstArgument.name; - } - // Verify that wrapBuilder's arguments are decorated with @Builder - // rule1: The wrapBuilder accepts only a function decorated by '@Builder' - if (functionName && !builderFunctionNames.includes(functionName)) { - context.report({ - node: firstArgument, - message: rule.messages.invalidBuilderCheck, - }); - } - }); - }); -} -function validateWrapBuilder( - node: arkts.StructDeclaration, - builderFunctionNames: string[], - context: UISyntaxRuleContext -): void { - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; + // Collect all the function names that are decorated with @Builder + private collectBuilderFunctions(node: arkts.AstNode): void { + if (!arkts.isEtsScript(node)) { + return; + } + node.statements.forEach((statement) => { + if (!arkts.isFunctionDeclaration(statement)) { + return; + } + const buildDecoratorUsage = getFunctionAnnotationUsage(statement, PresetDecorators.BUILDER); + if (!buildDecoratorUsage) { + return; + } + const functionName = statement.scriptFunction.id?.name; + if (!functionName || functionName === '' || this.builderFunctionNames.includes(functionName)) { + return; + } + this.builderFunctionNames.push(functionName); + }); } - validateWrapBuilderArguments(member, context, builderFunctionNames); - }); -} -const rule: UISyntaxRule = { - name: 'wrap-builder-check', - messages: { - invalidBuilderCheck: 'The wrapBuilder accepts only a function decorated by @Builder.', - }, - setup(context) { - let builderFunctionNames: string[] = []; - return { - parsed: (node): void => { - if (arkts.isEtsScript(node)) { - collectBuilderFunctions(node, builderFunctionNames); + private validateWrapBuilderInIdentifier(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !node.expression) { + return; + } + // If the current node is not a wrap builder, return + if (!arkts.isIdentifier(node.expression) || getIdentifierName(node.expression) !== WRAP_BUILDER) { + return; } - if (!arkts.isStructDeclaration(node)) { - return; + let functionName: string = ''; + // Get the parameters of the wrap builder + node.arguments.forEach(argument => { + if (!arkts.isIdentifier(argument)) { + return; + } + functionName = argument.name; + }); + // If the function name is not empty and not decorated by the @builder, an error is reported + if (functionName === '' || !this.builderFunctionNames.includes(functionName)) { + const errorNode = node.arguments[0]; + this.report({ + node: errorNode, + message: this.messages.invalidWrapBuilderCheck, + }); } - validateWrapBuilder(node, builderFunctionNames, context); - }, - }; - }, -}; + } +} -export default rule; +export default StructNoExtendsRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts similarity index 76% rename from arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts rename to arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts index d8c642ed22902f185edc4b5e0d950da2d3665a02..0e86f815215f63a10f7e346303db0f5118a31509 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts @@ -17,9 +17,13 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxLinterVisitor } from './ui-syntax-linter-visitor'; export class ParsedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { - visitor(node: arkts.AstNode): arkts.AstNode { - this.processor.parsed(node); - node = this.visitEachChild(node); - return node; - } + handle(node: arkts.AstNode): void { + this.processor.parsed(node); + } +} + +export class CheckedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { + handle(node: arkts.AstNode): void { + this.processor.checked(node); + } } diff --git a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts index 356304f88f8ce0dd457aaff3c92c7ac66e14d3d3..33e1c85f1e6dac6cf820c9b6d8673e2898532981 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts @@ -13,11 +13,27 @@ * limitations under the License. */ +import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; import { UISyntaxRuleProcessor } from '../processor'; export abstract class UISyntaxLinterVisitor extends AbstractVisitor { - constructor(protected processor: UISyntaxRuleProcessor) { - super(); - } + constructor(protected processor: UISyntaxRuleProcessor) { + super(); + } + + visitor(node: arkts.AstNode): arkts.AstNode { + this.handle(node); + node = this.visitEachChild(node); + return node; + } + + transform(node: arkts.AstNode): arkts.AstNode { + this.processor.beforeTransform(); + const transformedNode = this.visitor(node); + this.processor.afterTransform(); + return transformedNode; + } + + abstract handle(node: arkts.AstNode): void; } diff --git a/arkui-plugins/ui-syntax-plugins/utils/index.ts b/arkui-plugins/ui-syntax-plugins/utils/index.ts index 87ba336a7ce55c0fcc12bce3a392b2b1d2dc0898..0e3f34341be809724e5b4355f51a60b3c9707c88 100644 --- a/arkui-plugins/ui-syntax-plugins/utils/index.ts +++ b/arkui-plugins/ui-syntax-plugins/utils/index.ts @@ -16,185 +16,542 @@ import * as arkts from '@koalaui/libarkts'; import * as fs from 'fs'; import * as path from 'path'; +import { UISyntaxRuleContext } from 'ui-syntax-plugins/rules/ui-syntax-rule'; + +export const EXCLUDE_EXTERNAL_SOURCE_PREFIXES: Array = [ + 'std', + 'escompat', + 'security', + 'application', + 'permissions', + 'bundleManager', + 'commonEvent', + 'global', + 'arkui', + /@arkts\..*/, + /@ohos\.*/, + /@system\..*/, + /@koalaui\./, + /ability\..*/, +]; + +export const BUILD_NAME: string = 'build'; + +export const SINGLE_CHILD_COMPONENT: number = 1; +export const MAX_ENTRY_DECORATOR_COUNT: number = 1; +export const MAX_PREVIEW_DECORATOR_COUNT: number = 10; + +export const COMPONENT_REPEAT: string = 'Repeat'; +export const TEMPLATE: string = 'template'; + +export const PresetType = { + STRING: 'string', + NUMBER: 'number', + BOOLEAN: 'boolean', + BIGINT: 'bigint', +}; + +export const forbiddenUseStateType: string[] = [ + 'Scroller', + 'SwiperScroller', + 'VideoController', + 'WebController', + 'CustomDialogController', + 'SwiperController', + 'TabsController', + 'CalendarController', + 'AbilityController', + 'XComponentController', + 'CanvasRenderingContext2D', + 'CanvasGradient', + 'ImageBitmap', + 'ImageData', + 'Path2D', + 'RenderingContextSettings', + 'OffscreenCanvasRenderingContext2D', + 'PatternLockController', + 'TextAreaController', + 'TextInputController', + 'TextTimerController', + 'SearchController', + 'RichEditorController', +]; export const PresetDecorators = { - BUILDER_PARAM: 'BuilderParam', - COMPONENT_V1: 'Component', - COMPONENT_V2: 'ComponentV2', - COMPUTED: 'Computed', - CONSUME: 'Consume', - CONSUMER: 'Consumer', - CUSTOM_DIALOG: 'CustomDialog', - ENTRY: 'Entry', - EVENT: 'Event', - PREVIEW: 'Preview', - STATE: 'State', - PARAM: 'Param', - PROP: 'Prop', - PROVIDE: 'Provide', - PROVIDER: 'Provider', - LINK: 'Link', - LOCAL: 'Local', - OBJECT_LINK: 'ObjectLink', - STORAGE_PROP: 'StorageProp', - STORAGE_LINK: 'StorageLink', - LOCAL_STORAGE_PROP: 'LocalStorageProp', - LOCAL_STORAGE_LINK: 'LocalStorageLink', - REQUIRE: 'Require', - REUSABLE_V1: 'Reusable', - REUSABLE_V2: 'ReusableV2', - OBSERVED_V1: 'Observed', - OBSERVED_V2: 'ObservedV2', - TYPE: 'Type', - WATCH: 'Watch', - BUILDER: 'Builder', - TRACK: 'Track', - TRACE: 'Trace', - ONCE: 'Once', - MONITOR: 'Monitor', - LOCAL_BUILDER: 'LocalBuilder', + TOGGLE: 'Toggle', + BUILDER_PARAM: 'BuilderParam', + COMPONENT_V1: 'Component', + COMPONENT_V2: 'ComponentV2', + COMPUTED: 'Computed', + CONSUME: 'Consume', + CONSUMER: 'Consumer', + CUSTOM_DIALOG: 'CustomDialog', + ENTRY: 'Entry', + EVENT: 'Event', + PREVIEW: 'Preview', + STATE: 'State', + PARAM: 'Param', + PROP_REF: 'PropRef', + PROVIDE: 'Provide', + PROVIDER: 'Provider', + LINK: 'Link', + LOCAL: 'Local', + OBJECT_LINK: 'ObjectLink', + STORAGE_PROP_REF: 'StoragePropRef', + STORAGE_LINK: 'StorageLink', + LOCAL_STORAGE_LINK: 'LocalStorageLink', + REQUIRE: 'Require', + REUSABLE_V1: 'Reusable', + REUSABLE_V2: 'ReusableV2', + OBSERVED_V1: 'Observed', + OBSERVED_V2: 'ObservedV2', + TYPE: 'Type', + WATCH: 'Watch', + BUILDER: 'Builder', + TRACK: 'Track', + TRACE: 'Trace', + ONCE: 'Once', + MONITOR: 'Monitor', + LOCAL_BUILDER: 'LocalBuilder', + REGULAR: 'regular', + VARIABLE: 'variable', + PARAMETER: 'parameter', + ANIMATABLE_EXTEND: 'AnimatableExtend', +}; + +export const TOGGLE_TYPE: string = 'ToggleType'; +export const TYPE: string = 'type'; +export const WRAP_BUILDER: string = 'wrapBuilder'; + +export const ToggleType = { + CHECKBOX: 'Checkbox', + BUTTON: 'Button', +}; + +export const ReuseConstants = { + REUSE: 'reuse', + REUSE_ID: 'reuseId', }; -const PUBLIC_PROPERTY_MODIFIERS: Number = 4; -const PROTECTED_PROPERTY_MODIFIERS: Number = 8; -const PRIVATE_PROPERTY_MODIFIERS: Number = 16; +export function isClassPropertyOptional(node: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL); +} + export function getIdentifierName(node: arkts.AstNode): string { - if (!arkts.isIdentifier(node)) { - throw new Error(`Except a Identifier type!`); - } - return node.name; + if (!arkts.isIdentifier(node)) { + return ''; + } + return node.name; } export function getAnnotationName(annotation: arkts.AnnotationUsage): string { - if (!annotation.expr) { - throw new Error(`The expr property does not exist!`); - } - return getIdentifierName(annotation.expr); + if (!annotation.expr) { + return ''; + } + return getIdentifierName(annotation.expr); } export function getAnnotationUsage( - declaration: arkts.StructDeclaration, - annotationName: string, + declaration: arkts.StructDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName, - ); + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName + ); } export function getClassAnnotationUsage( - declaration: arkts.ClassDeclaration, - annotationName: string, + declaration: arkts.ClassDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - if (!declaration.definition || !declaration.definition.annotations) { - return undefined; - } - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || - (arkts.isCallExpression(annotation.expr) && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName)) - ); + if (!declaration.definition || !declaration.definition.annotations) { + return undefined; + } + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && + ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || + (arkts.isCallExpression(annotation.expr) && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === annotationName)) + ); } +export function getClassPropertyName(property: arkts.ClassProperty): string | undefined { + if (!property.key) { + return undefined; + } + return getIdentifierName(property.key); +} +export function getClassPropertyType(property: arkts.ClassProperty): string | undefined { + return property.typeAnnotation?.dumpSrc(); +} + +export function getClassPropertyAnnotationNames(property: arkts.ClassProperty): string[] { + return property.annotations.map((annotation) => getAnnotationName(annotation)); +} -export function getClassPropertyName(property: arkts.ClassProperty): string { - return getIdentifierName(property.key); +export function getClassPropertyAnnotation( + property: arkts.ClassProperty, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return property.annotations?.find(annotation => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } -export function getClassPropertyType(property: arkts.ClassProperty): string { - return property.typeAnnotation.dumpSrc(); +export function getClassDeclarationAnnotation( + classDeclaration: arkts.ClassDeclaration, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return classDeclaration.definition?.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } -export function getClassPropertyAnnotationNames( - property: arkts.ClassProperty, -): string[] { - return property.annotations.map((annotation) => - getAnnotationName(annotation), - ); +export function findDecorator( + member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string +): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); } export function isPublicClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PUBLIC_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC); } export function isPrivateClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PRIVATE_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE); } -export function isProtectedlassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PROTECTED_PROPERTY_MODIFIERS; +export function isProtectedClassProperty(property: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED); +} + +export function listToString(strList: string[]): string { + return strList.length > 1 ? `${strList.join(',')}` : strList.join(''); } export class MultiMap { - private readonly map: Map; - constructor() { - this.map = new Map(); - } - /** - * Add key-value pairs to MultiMap - * @param key key - * @param value value - */ - add(key: K, value: V): void { - if (!this.map.has(key)) { - this.map.set(key, []); - } - this.map.get(key)!.push(value); - } - - /** - * Gets all the values of the specified key - * @param key key - * @returns An array of values, which returns an empty array if the key does not exist - */ - get(key: K): V[] { - return this.map.get(key) || []; - } - - /** - * Check if the specified key exists in the MultiMap - * @param key key - * @returns Whether it exists - */ - has(key: K): boolean { - return this.map.has(key); - } + private readonly map: Map; + constructor() { + this.map = new Map(); + } + /** + * Add key-value pairs to MultiMap + * @param key key + * @param value value + */ + add(key: K, value: V): void { + if (!this.map.has(key)) { + this.map.set(key, []); + } + this.map.get(key)!.push(value); + } + + /** + * Gets all the values of the specified key + * @param key key + * @returns An array of values, which returns an empty array if the key does not exist + */ + get(key: K): V[] { + return this.map.get(key) || []; + } + + /** + * Check if the specified key exists in the MultiMap + * @param key key + * @returns Whether it exists + */ + has(key: K): boolean { + return this.map.has(key); + } +} + +export function hasAnnotation(annoArray: readonly arkts.AnnotationUsage[], annotationName: string): boolean { + return (annoArray || []).some((anno) => anno.expr && getIdentifierName(anno.expr) === annotationName); } interface ComponentJson { - name: string; - atomic?: boolean; - attrs: string[]; -} - -export function getContainerComponents(dirPath: string): Set { - const resultSet = new Set(); - const absolutePath = path.resolve(__dirname, dirPath); - - if (!fs.existsSync(absolutePath)) { - throw new Error(`Directory does not exist: ${absolutePath}`); - } - // Read all files in the directory - const files = fs.readdirSync(absolutePath); - - files.forEach(file => { - if (path.extname(file) === '.json') { - const filePath = path.join(absolutePath, file); - const fileContent = fs.readFileSync(filePath, 'utf-8'); - const componentJson: ComponentJson = JSON.parse(fileContent); - if ((!componentJson.atomic || componentJson.atomic !== true) && (componentJson.name)) { - resultSet.add(componentJson.name); - } - } - }); - return resultSet; + name: string; + atomic?: boolean; + attrs: string[]; + single?: boolean; + parents?: string[]; + children?: string[]; +} + +export interface UISyntaxRuleComponents { + builtInAttributes: string[]; + containerComponents: string[]; + atomicComponents: string[]; + singleChildComponents: string[]; + validParentComponent: Map; + validChildComponent: Map; +} + +export function getUIComponents(dirPath: string): UISyntaxRuleComponents | undefined { + const absolutePath = path.resolve(__dirname, dirPath); + let builtInAttributes: string[] = []; + let containerComponents: string[] = []; + let atomicComponents: string[] = []; + let singleChildComponents: string[] = []; + let validParentComponent: Map = new Map(); + let validChildComponent: Map = new Map(); + + if (!fs.existsSync(absolutePath)) { + return undefined; + } + // Read all files in the directory + const files = fs.readdirSync(absolutePath); + + files.forEach((file) => { + if (path.extname(file) === '.json') { + const filePath = path.join(absolutePath, file); + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const componentJson: ComponentJson = JSON.parse(fileContent); + // Record the container component name + if ((!componentJson.atomic || componentJson.atomic !== true) && componentJson.name) { + containerComponents.push(componentJson.name); + } + // Record the atomic component name + if (componentJson.atomic && componentJson.atomic === true && componentJson.name) { + atomicComponents.push(componentJson.name); + } + // Record the name of a single subcomponent component name + if (componentJson.single && componentJson.single === true && componentJson.name) { + singleChildComponents.push(componentJson.name); + } + // Record a valid parent component name + if (componentJson.parents && componentJson.name) { + validParentComponent.set(componentJson.name, componentJson.parents); + } + // Record a valid children component name + if (componentJson.children && componentJson.name) { + validChildComponent.set(componentJson.name, componentJson.children); + } + // Document all built-in attributes + componentJson.attrs + ?.filter((attr) => !builtInAttributes.includes(attr)) + .forEach((attr) => builtInAttributes.push(attr)); + } + }); + const componentsInfo: UISyntaxRuleComponents = { + builtInAttributes, + containerComponents, + atomicComponents, + singleChildComponents, + validParentComponent, + validChildComponent, + }; + + return componentsInfo; +} + +export function getConsistentResourceInfo(): Map { + const resultMap = new Map(); + const consistentResourcePath = + path.resolve(__dirname, '../../../../../../../previewer/common/resources/entry/resources.txt'); + let resourceText: string = ''; + try { + // The contents of the file are read synchronously + resourceText = fs.readFileSync(path.resolve(consistentResourcePath), 'utf-8'); + } catch (error: unknown) { + return resultMap; + } + // Split text by line + const lines = resourceText.split('\n'); + for (const line of lines) { + // Skip blank lines + if (!line.trim()) { + continue; + } + const match = line.match(/id:(\d+),\s*'([^']+)'\s*'([^']+)'/); + if (match && match.length === 4) { + const id = match[1]; + const value = match[2]; + const resourceName = match[3]; + // Remove resource names that start with 'ohos_id' or 'ohos_fa' + if (resourceName.startsWith('ohos_id') || resourceName.startsWith('ohos_fa')) { + continue; + } + let entries = resultMap.get(value); + if (!entries) { + entries = []; + resultMap.set(value, entries); + } + entries.push({ + id: id, + resourceName: resourceName, + }); + } + } + return resultMap; +} + +export function isBuiltInAttribute(context: UISyntaxRuleContext, attributeName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.builtInAttributes.includes(attributeName); +} +export function isBuildInComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return ( + context.componentsInfo.containerComponents.includes(componentName) || + context.componentsInfo.atomicComponents.includes(componentName) + ); +} + +export function isAtomicComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.atomicComponents.includes(componentName); +} + +export function isContainerComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.containerComponents.includes(componentName); +} + +export function isSingleChildComponent(context: UISyntaxRuleContext, componentName: string): boolean { + if (!context.componentsInfo) { + return false; + } + return context.componentsInfo.singleChildComponents.includes(componentName); +} + +export function readJSON(path: string): T | null { + if (!fs.existsSync(path)) { + return null; + } + const content = fs.readFileSync(path).toString(); + if (!content) { + return null; + } + return JSON.parse(content) as T; +} + +export function tracePerformance any>(name: string, fn: T): T { + return function (this: ThisParameterType, ...args: Parameters): ReturnType { + arkts.Performance.getInstance().createEvent(name); + const result = fn.apply(this, args); + arkts.Performance.getInstance().stopEvent(name, true); + return result; + } as T; +} + +const ANNOTATION_PRESET_MODULE_PREFIXES: string[] = ['arkui.', '@ohos.', '@kit.ArkUI']; + +export function isFromPresetModules(moduleName: string): boolean { + for (const presetModulePrefix of ANNOTATION_PRESET_MODULE_PREFIXES) { + if (moduleName.startsWith(presetModulePrefix)) { + return true; + } + } + return false; +} + +export function getAnnotationUsagesByName( + annotations: readonly arkts.AnnotationUsage[], + annotationNames: string[] +): Array { + return annotationNames.map((annotationName) => getAnnotationUsageByName(annotations, annotationName)); +} + +export function getAnnotationUsageByName( + annotations: readonly arkts.AnnotationUsage[], + annotationName: string +): arkts.AnnotationUsage | undefined { + return annotations.find((annotation: arkts.AnnotationUsage): boolean => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr) || annotation.expr.name !== annotationName) { + return false; + } + const annotationDeclaration = arkts.getDecl(annotation.expr); + if (!annotationDeclaration) { + return false; + } + const program = arkts.getProgramFromAstNode(annotationDeclaration); + if (!isFromPresetModules(program.moduleName)) { + return false; + } + return true; + }); +} + +export function isStructClassDeclaration(node: arkts.AstNode): node is arkts.ClassDeclaration { + return ( + arkts.isClassDeclaration(node) && !!node.definition && arkts.classDefinitionIsFromStructConst(node.definition) + ); +} + +export function getFunctionAnnotationUsage( + declaration: arkts.FunctionDeclaration, + annotationName: string, +): arkts.AnnotationUsage | undefined { + if (!declaration || !declaration.annotations) { + return undefined; + } + return declaration.annotations.find( + (annotation) => + annotation.expr && + ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || + (arkts.isCallExpression(annotation.expr) && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === annotationName)) + ); +} + +export function getCallee(callExpression: arkts.CallExpression): arkts.Identifier | undefined { + const expression = callExpression.expression; + if (arkts.isIdentifier(expression)) { + return expression; + } + if (arkts.isMemberExpression(expression)) { + if (arkts.isCallExpression(expression.object)) { + return getCallee(expression.object); + } + } + return undefined; +} + +export const TypeFlags = { + Boolean: 'boolean', + String: 'string', + Number: 'number', + Enum: 'enum', + Null: 'null', + Undefined: 'undefined', + Object: 'object', + Array: 'array', + Function: 'function', + Symbol: 'symbol', + BigInt: 'bigint', + Unknown: 'unknown', + Any: 'any', + Never: 'never', + Void: 'void', + This: 'this', + TypeParameter: 'typeParameter', + Literal: 'literal', + Union: 'union', +}; + +export function getCurrentFilePath(node: arkts.AstNode): string | undefined { + const program = arkts.getProgramFromAstNode(node); + return program.absName; } \ No newline at end of file diff --git a/compiler/package-lock.json b/compiler/package-lock.json index 3aa764b4f0c25761cf133f609f837ca5b445faa6..3eaad929521571dfd04c648b3b669b4594ae16b8 100644 --- a/compiler/package-lock.json +++ b/compiler/package-lock.json @@ -1538,6 +1538,43 @@ "picomatch": "^2.3.1" } }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "requires": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + }, + "dependencies": { + "type-detect": { + "version": "4.1.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==" + } + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==" + }, "@types/eslint": { "version": "8.40.2", "resolved": "https://repo.huaweicloud.com/repository/npm/@types/eslint/-/eslint-8.40.2.tgz", @@ -2702,6 +2739,16 @@ "flat-cache": "^3.0.4" } }, + "fill-keys": { + "version": "1.0.2", + "resolved": "https://repo.huaweicloud.com/repository/npm/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA==", + "dev": true, + "requires": { + "is-object": "~1.0.1", + "merge-descriptors": "~1.0.0" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.0.1.tgz", @@ -3071,6 +3118,12 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-object": { + "version": "1.0.2", + "resolved": "https://repo.huaweicloud.com/repository/npm/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "dev": true + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://repo.huaweicloud.com/repository/npm/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -3207,6 +3260,11 @@ "graceful-fs": "^4.1.6" } }, + "just-extend": { + "version": "6.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz", @@ -3317,6 +3375,12 @@ } } }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3501,6 +3565,12 @@ } } }, + "module-not-found-error": { + "version": "1.0.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.2.tgz", @@ -3523,6 +3593,28 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "nise": { + "version": "5.1.9", + "resolved": "https://repo.huaweicloud.com/repository/npm/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + }, + "dependencies": { + "@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://repo.huaweicloud.com/repository/npm/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "requires": { + "@sinonjs/commons": "^3.0.1" + } + } + } + }, "node-releases": { "version": "2.0.12", "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.12.tgz", @@ -3631,6 +3723,11 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/path-type/-/path-type-4.0.0.tgz", @@ -3684,6 +3781,17 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "proxyquire": { + "version": "2.1.3", + "resolved": "https://repo.huaweicloud.com/repository/npm/proxyquire/-/proxyquire-2.1.3.tgz", + "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", + "dev": true, + "requires": { + "fill-keys": "^1.0.2", + "module-not-found-error": "^1.0.1", + "resolve": "^1.11.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://repo.huaweicloud.com/repository/npm/punycode/-/punycode-2.3.0.tgz", @@ -3930,6 +4038,26 @@ "resolved": "https://repo.huaweicloud.com/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "sinon": { + "version": "15.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "dependencies": { + "diff": { + "version": "5.2.0", + "resolved": "https://repo.huaweicloud.com/repository/npm/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==" + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", @@ -4096,8 +4224,7 @@ "type-detect": { "version": "4.0.8", "resolved": "https://repo.huaweicloud.com/repository/npm/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-fest": { "version": "0.20.2", diff --git a/koala-wrapper/koalaui/interop/src/cpp/crashdump.h b/koala-wrapper/koalaui/interop/src/cpp/crashdump.h index 7d1bac04091f24210182e07dbba90b5a21e9e7fa..467e57abff23a73e72d8b1b3e564db7d389586b0 100644 --- a/koala-wrapper/koalaui/interop/src/cpp/crashdump.h +++ b/koala-wrapper/koalaui/interop/src/cpp/crashdump.h @@ -26,7 +26,9 @@ static void onCrashHandler(int signo) { void* stack[20]; size_t size = backtrace(stack, 20); backtrace_symbols_fd(stack, size, STDERR_FILENO); - if (oldCrashHandler) oldCrashHandler(signo); + if (oldCrashHandler) { + oldCrashHandler(signo); + } } static void installCrashHandlers() { diff --git a/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc b/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc index 103c8c7de10fdeda4085be4bf9a1266dcca627ef..751326493a87a51ca7fc71200f3156bbff5b51a0 100644 --- a/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc +++ b/koala-wrapper/koalaui/interop/src/cpp/ets/convertors-ets.cc @@ -30,7 +30,7 @@ static bool registerNatives(ets_env *env, const ets_class clazz, const std::vect std::vector methods; methods.reserve(impls.size()); bool result = true; - for (const auto &[name, type, func, flag] : impls) { + for (const auto &[name, _, func, flag] : impls) { EtsNativeMethod method; method.name = name.c_str(); method.func = func; diff --git a/koala-wrapper/native/BUILD.gn b/koala-wrapper/native/BUILD.gn index a79c063ecef2336c3ea7d9e7250e8c875e227050..ab7989a90c24d7b465c3b294fd77706b9a75e043 100644 --- a/koala-wrapper/native/BUILD.gn +++ b/koala-wrapper/native/BUILD.gn @@ -25,6 +25,7 @@ shared_library("es2panda") { "./src/bridges.cc", "./src/common.cc", "./src/generated/bridges.cc", + "./src/memoryTracker.cc" ] include_dirs = [ @@ -113,7 +114,6 @@ shared_library("es2panda") { "-Xclang", "-instcombine-lower-dbg-declare=0", "-no-canonical-prefixes", - "-static", "-rtlib=compiler-rt", "-stdlib=libc++", "-lunwind", @@ -124,13 +124,6 @@ shared_library("es2panda") { "-fno-strict-aliasing", "-Wno-builtin-macro-redefined", "-fms-extensions", - "-static", - "-rtlib=compiler-rt", - "-stdlib=libc++", - "-std=c++17", - "-lunwind", - "-lpthread", - "-Qunused-arguments", "-target", "x86_64-pc-windows-gnu", "-D__CUSTOM_SECURITY_LIBRARY", diff --git a/koala-wrapper/native/include/common.h b/koala-wrapper/native/include/common.h index 4c17074fb65791c8e4991ac78114da85a9b8e42f..2803e71afb507bb20963e59965e9eadb6e235288 100644 --- a/koala-wrapper/native/include/common.h +++ b/koala-wrapper/native/include/common.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef COMMON_H -#define COMMON_H +#ifndef COMMON_H_ +#define COMMON_H_ #include "dynamic-loader.h" #include "es2panda_lib.h" @@ -24,16 +24,16 @@ #include #include -using std::string, std::cout, std::endl, std::vector; +using std::string; es2panda_Impl *GetImpl(); -string getString(KStringPtr ptr); +string getString(const KStringPtr &ptr); -char* getStringCopy(KStringPtr& ptr); +char* getStringCopy(const KStringPtr& ptr); -inline KUInt unpackUInt(const KByte* bytes); +KUInt unpackUInt(const KByte* bytes); es2panda_ContextState intToState(KInt state); -#endif // COMMON_H \ No newline at end of file +#endif // COMMON_H_ \ No newline at end of file diff --git a/koala-wrapper/native/include/memoryTracker.h b/koala-wrapper/native/include/memoryTracker.h new file mode 100644 index 0000000000000000000000000000000000000000..3f0dc71ebb345e941cec8dc5ae94d779543c684f --- /dev/null +++ b/koala-wrapper/native/include/memoryTracker.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 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. + */ + +#ifndef KOALA_MEMORY_TRACKER +#define KOALA_MEMORY_TRACKER + +#include +#include + +// 内存统计结构体 +struct MemoryStats { + size_t currentRss = 0; // 当前驻留集大小 (字节) + size_t peakRss = 0; // 峰值驻留集大小 (字节) + size_t currentVss = 0; // 当前虚拟内存大小 (字节) + size_t pageFaultsMinor = 0; // 小页错误次数 + size_t pageFaultsMajor = 0; // 大页错误次数 +}; + +class MemoryTracker { +public: + MemoryTracker() + { + Reset(); + } + + void Reset(); + MemoryStats GetDelta(); + + template + MemoryStats MeasureMemory(Func&& func); + + void Report(MemoryStats stats); + +private: + MemoryStats baseline; +}; + +MemoryStats GetMemoryStats(); + +#endif diff --git a/koala-wrapper/native/src/bridges.cc b/koala-wrapper/native/src/bridges.cc index 1db37cc8e0a435734cdf09c7f7102dbd66da786e..384054dcce460af62184eb17632f4cdddb10db0d 100644 --- a/koala-wrapper/native/src/bridges.cc +++ b/koala-wrapper/native/src/bridges.cc @@ -15,6 +15,61 @@ #include "common.h" +#include +#include +#include +#include "memoryTracker.h" + +KInt impl_ClassDefinitionLanguageConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + auto result = GetImpl()->ClassDefinitionLanguageConst(_context, _receiver); + return result; +} +KOALA_INTEROP_2(ClassDefinitionLanguageConst, KInt, KNativePointer, KNativePointer); + +KNativePointer impl_CreateClassDefinition3(KNativePointer context, KNativePointer ident, KNativePointer typeParams, KNativePointer superTypeParams, KNativePointerArray _implements, KUInt _implementsSequenceLength, KNativePointer ctor, KNativePointer superClass, KNativePointerArray body, KUInt bodySequenceLength, KInt modifiers, KInt flags, KInt lang) +{ + const auto _context = reinterpret_cast(context); + const auto _ident = reinterpret_cast(ident); + const auto _typeParams = reinterpret_cast(typeParams); + const auto _superTypeParams = reinterpret_cast(superTypeParams); + const auto __implements = reinterpret_cast(_implements); + const auto __implementsSequenceLength = static_cast(_implementsSequenceLength); + const auto _ctor = reinterpret_cast(ctor); + const auto _superClass = reinterpret_cast(superClass); + const auto _body = reinterpret_cast(body); + const auto _bodySequenceLength = static_cast(bodySequenceLength); + const auto _modifiers = static_cast(modifiers); + const auto _flags = static_cast(flags); + const auto _lang = static_cast(lang); + auto result = GetImpl()->CreateClassDefinition3(_context, _ident, _typeParams, _superTypeParams, __implements, __implementsSequenceLength, _ctor, _superClass, _body, _bodySequenceLength, _modifiers, _flags, _lang); + return result; +} +KOALA_INTEROP_13(CreateClassDefinition3, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt, KInt, KInt); + +KNativePointer impl_UpdateClassDefinition3(KNativePointer context, KNativePointer original, KNativePointer ident, KNativePointer typeParams, KNativePointer superTypeParams, KNativePointerArray _implements, KUInt _implementsSequenceLength, KNativePointer ctor, KNativePointer superClass, KNativePointerArray body, KUInt bodySequenceLength, KInt modifiers, KInt flags, KInt lang) +{ + const auto _context = reinterpret_cast(context); + const auto _original = reinterpret_cast(original); + const auto _ident = reinterpret_cast(ident); + const auto _typeParams = reinterpret_cast(typeParams); + const auto _superTypeParams = reinterpret_cast(superTypeParams); + const auto __implements = reinterpret_cast(_implements); + const auto __implementsSequenceLength = static_cast(_implementsSequenceLength); + const auto _ctor = reinterpret_cast(ctor); + const auto _superClass = reinterpret_cast(superClass); + const auto _body = reinterpret_cast(body); + const auto _bodySequenceLength = static_cast(bodySequenceLength); + const auto _modifiers = static_cast(modifiers); + const auto _flags = static_cast(flags); + const auto _lang = static_cast(lang); + auto result = GetImpl()->UpdateClassDefinition3(_context, _original, _ident, _typeParams, _superTypeParams, __implements, __implementsSequenceLength, _ctor, _superClass, _body, _bodySequenceLength, _modifiers, _flags, _lang); + return result; +} +KOALA_INTEROP_14(UpdateClassDefinition3, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt, KInt, KInt); + KBoolean impl_ClassDefinitionIsFromStructConst(KNativePointer contextPtr, KNativePointer instancePtr) { auto context = reinterpret_cast(contextPtr); @@ -178,7 +233,7 @@ KOALA_INTEROP_1(ContextProgram, KNativePointer, KNativePointer) KNativePointer impl_ProgramAst(KNativePointer contextPtr, KNativePointer programPtr) { - auto context = reinterpret_cast(programPtr); + auto context = reinterpret_cast(contextPtr); auto program = reinterpret_cast(programPtr); return GetImpl()->ProgramAst(context, program); } @@ -198,6 +253,14 @@ KNativePointer impl_CreateContextFromString(KNativePointer configPtr, KStringPtr } KOALA_INTEROP_3(CreateContextFromString, KNativePointer, KNativePointer, KStringPtr, KStringPtr) +KNativePointer impl_CreateContextFromStringWithHistory(KNativePointer configPtr, KStringPtr& sourcePtr, + KStringPtr& filenamePtr) +{ + auto config = reinterpret_cast(configPtr); + return GetImpl()->CreateContextFromStringWithHistory(config, sourcePtr.data(), filenamePtr.data()); +} +KOALA_INTEROP_3(CreateContextFromStringWithHistory, KNativePointer, KNativePointer, KStringPtr, KStringPtr) + KNativePointer impl_CreateContextFromFile(KNativePointer configPtr, KStringPtr& filenamePtr) { auto config = reinterpret_cast(configPtr); @@ -260,12 +323,31 @@ static KNativePointer impl_ProgramExternalSources(KNativePointer contextPtr, KNa { auto context = reinterpret_cast(contextPtr); auto&& instance = reinterpret_cast(instancePtr); - std::size_t source_len = 0; - auto external_sources = GetImpl()->ProgramExternalSources(context, instance, &source_len); - return new std::vector(external_sources, external_sources + source_len); + std::size_t sourceLen = 0; + auto externalSources = GetImpl()->ProgramExternalSources(context, instance, &sourceLen); + return new std::vector(externalSources, externalSources + sourceLen); } KOALA_INTEROP_2(ProgramExternalSources, KNativePointer, KNativePointer, KNativePointer); +static KNativePointer impl_ProgramDirectExternalSources(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto&& instance = reinterpret_cast(instancePtr); + std::size_t sourceLen = 0; + auto externalSources = GetImpl()->ProgramDirectExternalSources(context, instance, &sourceLen); + return new std::vector(externalSources, externalSources + sourceLen); +} +KOALA_INTEROP_2(ProgramDirectExternalSources, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_ProgramModuleNameConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto program = reinterpret_cast(instancePtr); + auto result = GetImpl()->ProgramModuleNameConst(context, program); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramModuleNameConst, KNativePointer, KNativePointer, KNativePointer); + static KNativePointer impl_ExternalSourceName(KNativePointer instance) { auto&& _instance_ = reinterpret_cast(instance); @@ -277,9 +359,9 @@ KOALA_INTEROP_1(ExternalSourceName, KNativePointer, KNativePointer); static KNativePointer impl_ExternalSourcePrograms(KNativePointer instance) { auto&& _instance_ = reinterpret_cast(instance); - std::size_t program_len = 0; - auto programs = GetImpl()->ExternalSourcePrograms(_instance_, &program_len); - return new std::vector(programs, programs + program_len); + std::size_t programLen = 0; + auto programs = GetImpl()->ExternalSourcePrograms(_instance_, &programLen); + return new std::vector(programs, programs + programLen); } KOALA_INTEROP_1(ExternalSourcePrograms, KNativePointer, KNativePointer); @@ -328,13 +410,21 @@ KBoolean impl_IsETSFunctionType(KNativePointer nodePtr) KOALA_INTEROP_1(IsETSFunctionType, KBoolean, KNativePointer) KInt impl_GenerateTsDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputDeclEts, KStringPtr &outputEts, - KBoolean exportAll, KStringPtr &recordFile) + KBoolean exportAll, KBoolean isolated, KStringPtr &recordFile) { auto context = reinterpret_cast(contextPtr); return GetImpl()->GenerateTsDeclarationsFromContext(context, outputDeclEts.data(), outputEts.data(), - exportAll, recordFile.data()); + exportAll, isolated, recordFile.data()); } -KOALA_INTEROP_5(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KStringPtr) +KOALA_INTEROP_6(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KBoolean, + KStringPtr) + +KInt impl_GenerateStaticDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputPath) +{ + auto context = reinterpret_cast(contextPtr); + return GetImpl()->GenerateStaticDeclarationsFromContext(context, outputPath.data()); +} +KOALA_INTEROP_2(GenerateStaticDeclarationsFromContext, KInt, KNativePointer, KStringPtr) void impl_InsertETSImportDeclarationAndParse(KNativePointer context, KNativePointer program, KNativePointer importDeclaration) @@ -377,6 +467,14 @@ KInt impl_SourcePositionLine(KNativePointer context, KNativePointer instance) } KOALA_INTEROP_2(SourcePositionLine, KInt, KNativePointer, KNativePointer); +KInt impl_SourcePositionCol(KNativePointer context, KNativePointer instance) +{ + auto&& _context_ = reinterpret_cast(context); + auto&& _instance_ = reinterpret_cast(instance); + return GetImpl()->SourcePositionCol(_context_, _instance_); +} +KOALA_INTEROP_2(SourcePositionCol, KInt, KNativePointer, KNativePointer); + KNativePointer impl_CreateSourceRange(KNativePointer context, KNativePointer start, KNativePointer end) { auto&& _context_ = reinterpret_cast(context); @@ -412,6 +510,14 @@ KNativePointer impl_ProgramFileNameWithExtensionConst(KNativePointer contextPtr, } KOALA_INTEROP_2(ProgramFileNameWithExtensionConst, KNativePointer, KNativePointer, KNativePointer) +KBoolean impl_ProgramIsASTLoweredConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto &&instance = reinterpret_cast(instancePtr); + return GetImpl()->ProgramIsASTLoweredConst(context, instance); +} +KOALA_INTEROP_2(ProgramIsASTLoweredConst, KBoolean, KNativePointer, KNativePointer); + KNativePointer impl_ETSParserGetGlobalProgramAbsName(KNativePointer contextPtr) { auto context = reinterpret_cast(contextPtr); @@ -420,6 +526,15 @@ KNativePointer impl_ETSParserGetGlobalProgramAbsName(KNativePointer contextPtr) } KOALA_INTEROP_1(ETSParserGetGlobalProgramAbsName, KNativePointer, KNativePointer) +KNativePointer impl_ProgramAbsoluteNameConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto &&instance = reinterpret_cast(instancePtr); + auto result = GetImpl()->ProgramAbsoluteNameConst(context, instance); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramAbsoluteNameConst, KNativePointer, KNativePointer, KNativePointer); + KNativePointer impl_ClassVariableDeclaration(KNativePointer context, KNativePointer classInstance) { const auto _context = reinterpret_cast(context); @@ -490,6 +605,7 @@ KNativePointer impl_SourceRangeEnd(KNativePointer context, KNativePointer range) return result; } KOALA_INTEROP_2(SourceRangeEnd, KNativePointer, KNativePointer, KNativePointer) + bool impl_ClassPropertyIsDefaultAccessModifierConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -506,6 +622,16 @@ KNativePointer impl_AstNodeStartConst(KNativePointer context, KNativePointer rec } KOALA_INTEROP_2(AstNodeStartConst, KNativePointer, KNativePointer, KNativePointer); +void impl_AstNodeSetStart(KNativePointer context, KNativePointer receiver, KNativePointer start) +{ + auto _context = reinterpret_cast(context); + auto _receiver = reinterpret_cast(receiver); + auto _start = reinterpret_cast(start); + GetImpl()->AstNodeSetStart(_context, _receiver, _start); + return; +} +KOALA_INTEROP_V3(AstNodeSetStart, KNativePointer, KNativePointer, KNativePointer) + KNativePointer impl_AstNodeEndConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -514,6 +640,16 @@ KNativePointer impl_AstNodeEndConst(KNativePointer context, KNativePointer recei } KOALA_INTEROP_2(AstNodeEndConst, KNativePointer, KNativePointer, KNativePointer); +void impl_AstNodeSetEnd(KNativePointer context, KNativePointer receiver, KNativePointer end) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + const auto _end = reinterpret_cast(end); + GetImpl()->AstNodeSetEnd(_context, _receiver, _end); + return; +} +KOALA_INTEROP_V3(AstNodeSetEnd, KNativePointer, KNativePointer, KNativePointer); + KBoolean impl_IsArrayExpression(KNativePointer nodePtr) { auto node = reinterpret_cast(nodePtr); @@ -521,6 +657,15 @@ KBoolean impl_IsArrayExpression(KNativePointer nodePtr) } KOALA_INTEROP_1(IsArrayExpression, KBoolean, KNativePointer) +KNativePointer impl_CreateDiagnosticKind(KNativePointer context, KStringPtr& message, KInt type) +{ + const auto _context = reinterpret_cast(context); + const auto _message = getStringCopy(message); + const auto _type = static_cast(type); + return const_cast(GetImpl()->CreateDiagnosticKind(_context, _message, _type)); +} +KOALA_INTEROP_3(CreateDiagnosticKind, KNativePointer, KNativePointer, KStringPtr, KInt); + inline KUInt unpackUInt(const KByte* bytes) { const KUInt BYTE_0 = 0; @@ -535,3 +680,142 @@ inline KUInt unpackUInt(const KByte* bytes) | (bytes[BYTE_2] << BYTE_2_SHIFT) | (bytes[BYTE_3] << BYTE_3_SHIFT) ); } + +KNativePointer impl_CreateDiagnosticInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, + KInt argc, KNativePointer pos) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + const auto _pos = reinterpret_cast(pos); + const std::size_t headerLen = 4; + const char** _args = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argsPtr + position); + position += headerLen; + _args[i] = strdup(std::string(reinterpret_cast(argsPtr + position), strLen).c_str()); + position += strLen; + } + return GetImpl()->CreateDiagnosticInfo(_context, _kind, _args, argc, _pos); +} +KOALA_INTEROP_5(CreateDiagnosticInfo, KNativePointer, KNativePointer, KNativePointer, + KStringArray, KInt, KNativePointer); + +KNativePointer impl_CreateSuggestionInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, + KInt argc, KStringPtr& substitutionCode, KStringPtr& title, + KNativePointer range) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + const auto _range = reinterpret_cast(range); + const std::size_t headerLen = 4; + const char** _args = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argsPtr + position); + position += headerLen; + _args[i] = strdup(std::string(reinterpret_cast(argsPtr + position), strLen).c_str()); + position += strLen; + } + const auto _substitutionCode = getStringCopy(substitutionCode); + const auto _title = getStringCopy(title); + return GetImpl()->CreateSuggestionInfo(_context, _kind, _args, argc, _substitutionCode, _title, _range); +} +KOALA_INTEROP_7(CreateSuggestionInfo, KNativePointer, KNativePointer, KNativePointer, + KStringArray, KInt, KStringPtr, KStringPtr, KNativePointer); + +void impl_LogDiagnostic(KNativePointer context, KNativePointer kind, KStringArray argvPtr, + KInt argc, KNativePointer pos) +{ + auto&& _context_ = reinterpret_cast(context); + auto&& _kind_ = reinterpret_cast(kind); + auto&& _pos_ = reinterpret_cast(pos); + const std::size_t headerLen = 4; + const char** argv = new const char*[argc]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = unpackUInt(argvPtr + position); + position += headerLen; + argv[i] = strdup(std::string(reinterpret_cast(argvPtr + position), strLen).c_str()); + position += strLen; + } + GetImpl()->LogDiagnostic(_context_, _kind_, argv, argc, _pos_); +} +KOALA_INTEROP_V5(LogDiagnostic, KNativePointer, KNativePointer, KStringArray, KInt, KNativePointer); + +void impl_LogDiagnosticWithSuggestion(KNativePointer context, KNativePointer diagnosticInfo, + KNativePointer suggestionInfo) +{ + const auto _context = reinterpret_cast(context); + const auto _diagnosticInfo = reinterpret_cast(diagnosticInfo); + const auto _suggestionInfo = reinterpret_cast(suggestionInfo); + GetImpl()->LogDiagnosticWithSuggestion(_context, _diagnosticInfo, _suggestionInfo); +} +KOALA_INTEROP_V3(LogDiagnosticWithSuggestion, KNativePointer, KNativePointer, KNativePointer); + +KBoolean impl_CallExpressionIsTrailingCallConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + return GetImpl()->CallExpressionIsTrailingCallConst(_context, _receiver); +} +KOALA_INTEROP_2(CallExpressionIsTrailingCallConst, KBoolean, KNativePointer, KNativePointer); + +MemoryTracker tracker; +void impl_MemoryTrackerReset(KNativePointer context) +{ + tracker.Reset(); +} +KOALA_INTEROP_V1(MemoryTrackerReset, KNativePointer); + +void impl_MemoryTrackerGetDelta(KNativePointer context) +{ + tracker.Report(tracker.GetDelta()); +} +KOALA_INTEROP_V1(MemoryTrackerGetDelta, KNativePointer); + +void impl_MemoryTrackerPrintCurrent(KNativePointer context) +{ + tracker.Report(GetMemoryStats()); +} +KOALA_INTEROP_V1(MemoryTrackerPrintCurrent, KNativePointer); + +KNativePointer impl_CreateTypeNodeFromTsType(KNativePointer context, KNativePointer nodePtr) +{ + const auto _context = reinterpret_cast(context); + const auto _nodePtr = reinterpret_cast(nodePtr); + auto _tsType = GetImpl()->TypedTsType(_context, _nodePtr); + if (_tsType == nullptr) { + _tsType = GetImpl()->ExpressionTsType(_context, _nodePtr); + } + if (_tsType == nullptr) { + return nullptr; + } + const auto _nodeTsType = reinterpret_cast(_tsType); + auto _typeAnnotation = GetImpl()->CreateOpaqueTypeNode(_context, _nodeTsType); + return _typeAnnotation; +} +KOALA_INTEROP_2(CreateTypeNodeFromTsType, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_JsdocStringFromDeclaration(KNativePointer contextPtr, KNativePointer decl) +{ + auto context = reinterpret_cast(contextPtr); + auto node = reinterpret_cast(decl); + + auto result = GetImpl()->JsdocStringFromDeclaration(context, node); + return new std::string(result); +} +KOALA_INTEROP_2(JsdocStringFromDeclaration, KNativePointer, KNativePointer, KNativePointer); + +static KNativePointer impl_ProgramSourceFilePathConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto program = reinterpret_cast(instancePtr); + + auto result = GetImpl()->ProgramSourceFilePathConst(context, program); + return new std::string(result); +} +KOALA_INTEROP_2(ProgramSourceFilePathConst, KNativePointer, KNativePointer, KNativePointer); diff --git a/koala-wrapper/native/src/common.cc b/koala-wrapper/native/src/common.cc index 084c86ea58936cd3d2fa6188eef735fc9e681a20..29baa82db80f92c41980e9f36373686a97da7f75 100644 --- a/koala-wrapper/native/src/common.cc +++ b/koala-wrapper/native/src/common.cc @@ -31,8 +31,10 @@ static es2panda_Impl *impl = nullptr; #ifdef __x86_64__ #define PLUGIN_DIR "linux_host_tools" - #else + #elif defined(__aarch64__) #define PLUGIN_DIR "linux_arm64_host_tools" + #else + #error "Unsupported architecture!" #endif #define LIB_PREFIX "lib" @@ -40,6 +42,10 @@ static es2panda_Impl *impl = nullptr; #endif const char* LIB_ES2PANDA_PUBLIC = LIB_PREFIX "es2panda_public" LIB_SUFFIX; +constexpr const char* IS_UI_FLAG = "IS_UI_FLAG"; +constexpr const char* NOT_UI_FLAG = "NOT_UI_FLAG"; +const string MODULE_SUFFIX = ".d.ets"; +const string ARKUI = "arkui"; #ifdef KOALA_WINDOWS const char *SEPARATOR = "\\"; @@ -103,11 +109,13 @@ es2panda_ContextState intToState(KInt state) return es2panda_ContextState(state); } -string getString(KStringPtr ptr) { +string getString(const KStringPtr& ptr) +{ return ptr.data(); } -char* getStringCopy(KStringPtr& ptr) { +char* getStringCopy(const KStringPtr& ptr) +{ return strdup(ptr.c_str()); } @@ -128,6 +136,56 @@ inline KUInt unpackUInt(const KByte* bytes) { ); } +void impl_MemInitialize() +{ + GetImpl()->MemInitialize(); +} +KOALA_INTEROP_V0(MemInitialize) + +void impl_MemFinalize() +{ + GetImpl()->MemFinalize(); +} +KOALA_INTEROP_V0(MemFinalize) + +KNativePointer impl_CreateGlobalContext(KNativePointer configPtr, KStringArray externalFileListPtr, + KInt fileNum, KBoolean lspUsage) +{ + auto config = reinterpret_cast(configPtr); + + const std::size_t headerLen = 4; + + const char** externalFileList = new const char* [fileNum]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(fileNum); ++i) { + strLen = unpackUInt(externalFileListPtr + position); + position += headerLen; + externalFileList[i] = strdup(std::string( + reinterpret_cast(externalFileListPtr + position), strLen).c_str()); + position += strLen; + } + + return GetImpl()->CreateGlobalContext(config, externalFileList, fileNum, lspUsage); +} +KOALA_INTEROP_4(CreateGlobalContext, KNativePointer, KNativePointer, KStringArray, KInt, KBoolean) + +void impl_DestroyGlobalContext(KNativePointer globalContextPtr) +{ + auto context = reinterpret_cast(globalContextPtr); + GetImpl()->DestroyGlobalContext(context); +} +KOALA_INTEROP_V1(DestroyGlobalContext, KNativePointer) + +KNativePointer impl_CreateCacheContextFromFile(KNativePointer configPtr, KStringPtr& fileName, + KNativePointer globalContext, KBoolean isExternal) +{ + auto config = reinterpret_cast(configPtr); + auto context = reinterpret_cast(globalContext); + return GetImpl()->CreateCacheContextFromFile(config, getStringCopy(fileName), context, isExternal); +} +KOALA_INTEROP_4(CreateCacheContextFromFile, KNativePointer, KNativePointer, KStringPtr, KNativePointer, KBoolean) + KNativePointer impl_CreateConfig(KInt argc, KStringArray argvPtr) { const std::size_t headerLen = 4; @@ -200,8 +258,20 @@ TODO: NOT FROM API (shouldn't be there) ----------------------------------------------------------------------------------------------------------------------------- */ -es2panda_AstNode * cachedParentNode; -es2panda_Context * cachedContext; +KNativePointer impl_AstNodeProgram(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto _context = reinterpret_cast(contextPtr); + auto _receiver = reinterpret_cast(instancePtr); + + if (GetImpl()->AstNodeIsProgramConst(_context, _receiver)) { + return GetImpl()->ETSModuleProgram(_context, _receiver); + } + return impl_AstNodeProgram(_context, GetImpl()->AstNodeParent(_context, _receiver)); +} +KOALA_INTEROP_2(AstNodeProgram, KNativePointer, KNativePointer, KNativePointer) + +thread_local es2panda_AstNode *cachedParentNode; +thread_local es2panda_Context *cachedContext; static void changeParent(es2panda_AstNode *child) { @@ -236,7 +306,7 @@ KNativePointer impl_AstNodeUpdateChildren(KNativePointer contextPtr, KNativePoin } KOALA_INTEROP_2(AstNodeUpdateChildren, KNativePointer, KNativePointer, KNativePointer) -std::vector cachedChildren; +thread_local std::vector cachedChildren; static void visitChild(es2panda_AstNode *node) { cachedChildren.emplace_back(node); @@ -256,6 +326,43 @@ KNativePointer impl_AstNodeChildren( } KOALA_INTEROP_2(AstNodeChildren, KNativePointer, KNativePointer, KNativePointer) +static bool isUIHeaderFile(es2panda_Context* context, es2panda_Program* program) +{ + auto result = GetImpl()->ProgramFileNameWithExtensionConst(context, program); + string fileNameWithExtension(result); + result = GetImpl()->ProgramModuleNameConst(context, program); + string moduleName(result); + + return fileNameWithExtension.length() >= MODULE_SUFFIX.length() + && fileNameWithExtension.substr(fileNameWithExtension.length() - MODULE_SUFFIX.length()) == MODULE_SUFFIX + && moduleName.find(ARKUI) != std::string::npos; +} + +KBoolean impl_ProgramCanSkipPhases(KNativePointer context, KNativePointer program) +{ + KStringPtr isUiFlag(IS_UI_FLAG); + KStringPtr notUiFlag(NOT_UI_FLAG); + const auto _context = reinterpret_cast(context); + const auto _program = reinterpret_cast(program); + if (isUIHeaderFile(_context, _program)) { + return false; + } + std::size_t sourceLen; + const auto externalSources = reinterpret_cast + (GetImpl()->ProgramExternalSources(_context, _program, &sourceLen)); + for (std::size_t i = 0; i < sourceLen; ++i) { + std::size_t programLen; + auto programs = GetImpl()->ExternalSourcePrograms(externalSources[i], &programLen); + for (std::size_t j = 0; j < programLen; ++j) { + if (isUIHeaderFile(_context, programs[j])) { + return false; + } + } + } + return true; +} +KOALA_INTEROP_2(ProgramCanSkipPhases, KBoolean, KNativePointer, KNativePointer) + /* ----------------------------------------------------------------------------------------------------------------------------- */ diff --git a/koala-wrapper/native/src/generated/bridges.cc b/koala-wrapper/native/src/generated/bridges.cc index 5bcf9071ca1dde6a8d695cdd0a30476761f3263a..01b34d85ab7fdc7ec393e8b9d5c21f07411a392d 100644 --- a/koala-wrapper/native/src/generated/bridges.cc +++ b/koala-wrapper/native/src/generated/bridges.cc @@ -13,7 +13,7 @@ * limitations under the License. */ -#include +#include "common.h" KNativePointer impl_CreateLabelledStatement(KNativePointer context, KNativePointer ident, KNativePointer body) { @@ -298,24 +298,6 @@ KInt impl_ETSFunctionTypeIrFlags(KNativePointer context, KNativePointer receiver } KOALA_INTEROP_2(ETSFunctionTypeIrFlags, KInt, KNativePointer, KNativePointer); -KBoolean impl_ETSFunctionTypeIrIsThrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSFunctionTypeIrIsThrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ETSFunctionTypeIrIsThrowingConst, KBoolean, KNativePointer, KNativePointer); - -KBoolean impl_ETSFunctionTypeIrIsRethrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSFunctionTypeIrIsRethrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ETSFunctionTypeIrIsRethrowingConst, KBoolean, KNativePointer, KNativePointer); - KBoolean impl_ETSFunctionTypeIrIsExtensionFunctionConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -2707,16 +2689,6 @@ void impl_TSTypeAliasDeclarationSetTypeParameters(KNativePointer context, KNativ } KOALA_INTEROP_V3(TSTypeAliasDeclarationSetTypeParameters, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_TSTypeAliasDeclarationAnnotations(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - std::size_t length; - auto result = GetImpl()->TSTypeAliasDeclarationAnnotations(_context, _receiver, &length); - return new std::vector(result, result + length); -} -KOALA_INTEROP_2(TSTypeAliasDeclarationAnnotations, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_TSTypeAliasDeclarationAnnotationsConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -3251,24 +3223,6 @@ KBoolean impl_ScriptFunctionHasThrowStatementConst(KNativePointer context, KNati } KOALA_INTEROP_2(ScriptFunctionHasThrowStatementConst, KBoolean, KNativePointer, KNativePointer); -KBoolean impl_ScriptFunctionIsThrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ScriptFunctionIsThrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ScriptFunctionIsThrowingConst, KBoolean, KNativePointer, KNativePointer); - -KBoolean impl_ScriptFunctionIsRethrowingConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ScriptFunctionIsRethrowingConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ScriptFunctionIsRethrowingConst, KBoolean, KNativePointer, KNativePointer); - KBoolean impl_ScriptFunctionIsDynamicConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -3325,16 +3279,6 @@ void impl_ScriptFunctionAddFlag(KNativePointer context, KNativePointer receiver, } KOALA_INTEROP_V3(ScriptFunctionAddFlag, KNativePointer, KNativePointer, KInt); -void impl_ScriptFunctionAddModifier(KNativePointer context, KNativePointer receiver, KInt flags) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - const auto _flags = static_cast(flags); - GetImpl()->ScriptFunctionAddModifier(_context, _receiver, _flags); - return ; -} -KOALA_INTEROP_V3(ScriptFunctionAddModifier, KNativePointer, KNativePointer, KInt); - KUInt impl_ScriptFunctionFormalParamsLengthConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -6691,16 +6635,6 @@ KNativePointer impl_ImportDeclarationSpecifiersConst(KNativePointer context, KNa } KOALA_INTEROP_2(ImportDeclarationSpecifiersConst, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_ImportDeclarationSpecifiers(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - std::size_t length; - auto result = GetImpl()->ImportDeclarationSpecifiers(_context, _receiver, &length); - return new std::vector(result, result + length); -} -KOALA_INTEROP_2(ImportDeclarationSpecifiers, KNativePointer, KNativePointer, KNativePointer); - KBoolean impl_ImportDeclarationIsTypeKindConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -6787,15 +6721,6 @@ KNativePointer impl_UpdateETSImportDeclaration(KNativePointer context, KNativePo } KOALA_INTEROP_6(UpdateETSImportDeclaration, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt); -KNativePointer impl_ETSImportDeclarationAssemblerName(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSImportDeclarationAssemblerName(_context, _receiver); - return new std::string(result); -} -KOALA_INTEROP_2(ETSImportDeclarationAssemblerName, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_ETSImportDeclarationAssemblerNameConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); diff --git a/koala-wrapper/native/src/memoryTracker.cc b/koala-wrapper/native/src/memoryTracker.cc new file mode 100644 index 0000000000000000000000000000000000000000..c12fcd248f90e76e5ee4401c77796602366b6983 --- /dev/null +++ b/koala-wrapper/native/src/memoryTracker.cc @@ -0,0 +1,178 @@ +/** + * Copyright (c) 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. + */ + +#include "memoryTracker.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#elif defined(__APPLE__) + #include + #include +#elif defined(__linux__) + #include + #include +#endif + +constexpr const char* UNIT_K = "kB"; +constexpr size_t BYTES_PER_KB = 1024; +constexpr const char* MEMORY_STATUS_FILE = "/proc/self/status"; +const std::regex VM_RSS_REGEX(R"#(VmRSS:\s*(\d+)\s*([kKmMgG]?B))#", + std::regex_constants::ECMAScript | std::regex_constants::icase); +const std::regex VM_SIZE_REGEX(R"#(VmSize:\s*(\d+)\s*([kKmMgG]?B))#", + std::regex_constants::ECMAScript | std::regex_constants::icase); + +constexpr int MATCH_GROUP_VALUE = 1; +constexpr int MATCH_GROUP_UNIT = 2; +constexpr int MATCH_GROUP_SIZE = 3; + +#if defined(_WIN32) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), + reinterpret_cast(&pmc), sizeof(pmc))) { + stats.currentRss = pmc.WorkingSetSize; + stats.peakRss = pmc.PeakWorkingSetSize; + stats.currentVss = pmc.PrivateUsage; // 私有内存使用量 + stats.pageFaultsMinor = pmc.PageFaultCount; + // Windows API不直接提供主缺页错误计数 + stats.pageFaultsMajor = 0; + } + return stats; +} + +#elif defined(__APPLE__) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) == 0) { + stats.currentRss = 0; // macOS需要专用API获取当前内存 + stats.peakRss = ru.ru_maxrss; // macOS返回字节 + stats.pageFaultsMinor = ru.ru_minflt; + stats.pageFaultsMajor = ru.ru_majflt; + + // 获取当前内存使用 (macOS专用API) + task_basic_info info; + mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; + if (task_info(mach_task_self(), TASK_BASIC_INFO_64, + (task_info_t)&info, &count) == KERN_SUCCESS) { + stats.currentRss = info.resident_size; // 物理内存使用量 + stats.currentVss = info.virtual_size; // 虚拟内存总量 + } + } + return stats; +} + +#elif defined(__linux__) +MemoryStats GetMemoryStats() +{ + MemoryStats stats = {0, 0, 0, 0, 0}; + struct rusage ru; + if (getrusage(RUSAGE_SELF, &ru) == 0) { + stats.peakRss = static_cast(ru.ru_maxrss) * BYTES_PER_KB; // KB -> 字节 + stats.pageFaultsMinor = ru.ru_minflt; + stats.pageFaultsMajor = ru.ru_majflt; + } + std::ifstream statusFile(MEMORY_STATUS_FILE); + if (!statusFile) { + return stats; + } + std::string line; + std::smatch matches; + while (std::getline(statusFile, line)) { + if (std::regex_match(line, matches, VM_RSS_REGEX) && matches.size() >= MATCH_GROUP_SIZE) { + stats.currentRss = std::stoull(matches[MATCH_GROUP_VALUE].str()); + std::string unit = matches[MATCH_GROUP_UNIT].str(); + if (unit == UNIT_K) { + stats.currentRss *= BYTES_PER_KB; + } + } else if (std::regex_match(line, matches, VM_SIZE_REGEX) && matches.size() >= MATCH_GROUP_SIZE) { + stats.currentVss = std::stoull(matches[MATCH_GROUP_VALUE].str()); + std::string unit = matches[MATCH_GROUP_UNIT].str(); + if (unit == UNIT_K) { + stats.currentVss *= BYTES_PER_KB; + } + } + } + return stats; +} +#endif + +void MemoryTracker::Reset() +{ + baseline = GetMemoryStats(); +} + +MemoryStats MemoryTracker::GetDelta() +{ + MemoryStats current = GetMemoryStats(); + MemoryStats delta = { + current.currentRss - baseline.currentRss, + current.peakRss - baseline.peakRss, + current.currentVss - baseline.currentVss, + current.pageFaultsMinor - baseline.pageFaultsMinor, + current.pageFaultsMajor - baseline.pageFaultsMajor + }; + return delta; +} + +template +MemoryStats MemoryTracker::MeasureMemory(Func&& func) +{ + Reset(); + auto preStats = GetMemoryStats(); + func(); + auto postStats = GetMemoryStats(); + + return { + postStats.currentRss - preStats.currentRss, + postStats.peakRss - preStats.peakRss, + postStats.currentVss - preStats.currentVss, + postStats.pageFaultsMinor - preStats.pageFaultsMinor, + postStats.pageFaultsMajor - preStats.pageFaultsMajor + }; +} + +void MemoryTracker::Report(MemoryStats stats) +{ + auto formatBytes = [](size_t bytes) -> std::string { + const double kb = BYTES_PER_KB; + const double mb = kb * BYTES_PER_KB; + const double gb = mb * BYTES_PER_KB; + + if (bytes > gb) return std::to_string(bytes / gb) + " GB"; + if (bytes > mb) return std::to_string(bytes / mb) + " MB"; + if (bytes > kb) return std::to_string(bytes / kb) + " KB"; + return std::to_string(bytes) + " B"; + }; + + std::cout << "Current RSS: " << formatBytes(stats.currentRss) << "\n"; + std::cout << "Peak RSS : " << formatBytes(stats.peakRss) << "\n"; + std::cout << "VSS : " << formatBytes(stats.currentVss) << "\n"; + std::cout << "FaultsMinor: " << stats.pageFaultsMinor << "\n"; + std::cout << "FaultsMajor: " << stats.pageFaultsMajor << "\n"; + return; +} \ No newline at end of file diff --git a/koala-wrapper/package-lock.json b/koala-wrapper/package-lock.json deleted file mode 100644 index 497d96895139daba726dd66036f8c26e2147defa..0000000000000000000000000000000000000000 --- a/koala-wrapper/package-lock.json +++ /dev/null @@ -1,1792 +0,0 @@ -{ - "name": "@koalaui/libarkts", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@babel/cli": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/cli/-/cli-7.20.7.tgz", - "integrity": "sha512-WylgcELHB66WwQqItxNILsMlaTd8/SO6SgTTjMp4uCI7P4QyH1r3nqgFmO3BfM4AtfniHgFMH3EpYFj/zynBkQ==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.8", - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.2.0", - "make-dir": "^2.1.0", - "slash": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true - }, - "@babel/core": { - "version": "7.20.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "requires": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "requires": { - "@babel/types": "^7.27.3" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, - "requires": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/helpers": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "requires": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - } - }, - "@babel/parser": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "requires": { - "@babel/types": "^7.28.0" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - } - }, - "@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.20.13", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.28.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - } - }, - "@babel/types": { - "version": "7.28.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nicolo-ribaudo/chokidar-2": { - "version": "2.1.8-no-fsevents.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", - "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", - "dev": true, - "optional": true - }, - "@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==", - "dev": true - }, - "@types/node": { - "version": "18.19.119", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "optional": true, - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.25.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - } - }, - "caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true - }, - "chokidar": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-js-compat": { - "version": "3.44.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "dev": true, - "requires": { - "browserslist": "^4.25.1" - } - }, - "debug": { - "version": "4.4.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "requires": { - "ms": "^2.1.3" - } - }, - "electron-to-chromium": { - "version": "1.5.187", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", - "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", - "dev": true - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "optional": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.16.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "optional": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "optional": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node-addon-api": { - "version": "8.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "dev": true - }, - "node-releases": { - "version": "2.0.19", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://repo.huaweicloud.com/repository/npm/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "optional": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://repo.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "regexpu-core": { - "version": "6.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsgen": { - "version": "0.8.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true - }, - "regjsparser": { - "version": "0.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "requires": { - "jsesc": "~3.0.2" - }, - "dependencies": { - "jsesc": { - "version": "3.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true - } - } - }, - "resolve": { - "version": "1.22.10", - "resolved": "https://repo.huaweicloud.com/repository/npm/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "requires": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "optional": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "typescript": { - "version": "5.8.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } -} diff --git a/koala-wrapper/src/Es2pandaEnums.ts b/koala-wrapper/src/Es2pandaEnums.ts index bf2ce0783871630fbd77266c141a40a831a094df..4aa8f8c2d2bfda260a40c929543aefaf22033812 100644 --- a/koala-wrapper/src/Es2pandaEnums.ts +++ b/koala-wrapper/src/Es2pandaEnums.ts @@ -70,6 +70,7 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_UNDEFINED_LITERAL, AST_NODE_TYPE_NUMBER_LITERAL, AST_NODE_TYPE_OMITTED_EXPRESSION, + AST_NODE_TYPE_OVERLOAD_DECLARATION, AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION, AST_NODE_TYPE_PROPERTY, AST_NODE_TYPE_REGEXP_LITERAL, @@ -83,6 +84,7 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_ETS_UNDEFINED_TYPE, AST_NODE_TYPE_ETS_NEVER_TYPE, AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE, + AST_NODE_TYPE_ETS_INTRINSIC_NODE_TYPE, AST_NODE_TYPE_ETS_FUNCTION_TYPE, AST_NODE_TYPE_ETS_WILDCARD_TYPE, AST_NODE_TYPE_ETS_PRIMITIVE_TYPE, @@ -181,10 +183,18 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_OBJECT_PATTERN, AST_NODE_TYPE_SPREAD_ELEMENT, AST_NODE_TYPE_REST_ELEMENT, -} +}; export enum Es2pandaImportFlags { IMPORT_FLAGS_NONE, IMPORT_FLAGS_DEFAULT_IMPORT, IMPORT_FLAGS_IMPLICIT_PACKAGE_IMPORT, } + +export enum Es2pandaLanguage { + AS = 0, + JS = 1, + TS = 2, + ETS = 3, + COUNT = 4 +} \ No newline at end of file diff --git a/koala-wrapper/src/Es2pandaNativeModule.ts b/koala-wrapper/src/Es2pandaNativeModule.ts index 770e72aefe7571806ff11ba9960d7627107792a2..09bccfe0ca1b234d9007b933ef8f9687d85046dc 100644 --- a/koala-wrapper/src/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/Es2pandaNativeModule.ts @@ -16,14 +16,18 @@ import { KNativePointer as KPtr, KInt, + KStringPtr, KBoolean, KNativePointer, registerNativeModuleLibraryName, loadNativeModuleLibrary, KDouble, + KStringArrayPtr, + KUInt, } from '@koalaui/interop'; import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from './generated/Es2pandaNativeModule'; import * as path from 'path'; +import { PluginDiagnosticType } from './arkts-api/peers/DiagnosticKind'; // TODO: this type should be in interop export type KPtrArray = BigUint64Array; @@ -110,6 +114,9 @@ export class Es2pandaNativeModule { string[]): KPtr { throw new Error('Not implemented'); } + _CreateContextFromStringWithHistory(config: KPtr, source: String, filename: String): KPtr { + throw new Error('Not implemented'); + } _CreateContextFromFile(config: KPtr, filename: String): KPtr { throw new Error('Not implemented'); } @@ -741,7 +748,12 @@ export class Es2pandaNativeModule { _ProgramExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } - + _ProgramDirectExternalSources(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + _AstNodeProgram(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } _ExternalSourceName(instance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } @@ -750,16 +762,28 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _ProgramCanSkipPhases(context: KNativePointer, program: KNativePointer): boolean { + throw new Error('Not implemented'); + } + _GenerateTsDeclarationsFromContext( config: KPtr, outputDeclEts: String, outputEts: String, exportAll: KBoolean, + isolated: KBoolean, recordFile: String ): KPtr { throw new Error('Not implemented'); } + _GenerateStaticDeclarationsFromContext( + config: KPtr, + outputPath: String + ): KPtr { + throw new Error('Not implemented'); + } + _InsertETSImportDeclarationAndParse( context: KNativePointer, program: KNativePointer, @@ -781,6 +805,9 @@ export class Es2pandaNativeModule { _SourcePositionLine(context: KNativePointer, instance: KNativePointer): KInt { throw new Error('Not implemented'); } + _SourcePositionCol(context: KNativePointer, instance: KNativePointer): KInt { + throw new Error('Not implemented'); + } _CreateETSStringLiteralType(context: KNativePointer, str: String): KNativePointer { throw new Error('Not implemented'); } @@ -792,6 +819,17 @@ export class Es2pandaNativeModule { _ClassDefinitionSetFromStructModifier(context: KNativePointer, instance: KNativePointer): void { throw new Error('Not implemented'); } + + _CreateClassDefinition3(context: KNativePointer, ident: KNativePointer, typeParams: KNativePointer, superTypeParams: KNativePointer, _implements: BigUint64Array, _implementsSequenceLength: KUInt, ctor: KNativePointer, superClass: KNativePointer, body: BigUint64Array, bodySequenceLength: KUInt, modifiers: KInt, flags: KInt, lang: KInt): KNativePointer { + throw new Error("'CreateClassDefinition was not overloaded by native module initialization") + } + _UpdateClassDefinition3(context: KNativePointer, original: KNativePointer, ident: KNativePointer, typeParams: KNativePointer, superTypeParams: KNativePointer, _implements: BigUint64Array, _implementsSequenceLength: KUInt, ctor: KNativePointer, superClass: KNativePointer, body: BigUint64Array, bodySequenceLength: KUInt, modifiers: KInt, flags: KInt, lang: KInt): KNativePointer { + throw new Error("'UpdateClassDefinition was not overloaded by native module initialization") + } + + _ClassDefinitionLanguageConst(context: KNativePointer, receiver: KNativePointer): KInt { + throw new Error("'ClassDefinitionLanguageConst was not overloaded by native module initialization") + } _ProgramFileNameConst(context: KPtr, program: KPtr): KNativePointer { throw new Error('Not implemented'); @@ -801,10 +839,18 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _ProgramIsASTLoweredConst(context: KPtr, program: KPtr): KBoolean { + throw new Error('Not implemented'); + } + _ETSParserGetGlobalProgramAbsName(context: KNativePointer): KNativePointer { throw new Error('Not implemented'); } + _ProgramAbsoluteNameConst(context: KNativePointer, instance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + _ImportSpecifierIsRemovableConst(context: KNativePointer, instance: KNativePointer): KBoolean { throw new Error('Not implemented'); } @@ -821,18 +867,30 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _AstNodeSetStart(context: KNativePointer, receiver: KNativePointer, start: KNativePointer): void { + throw new Error('Not implemented'); + } + _AstNodeEndConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error('Not implemented'); } + _AstNodeSetEnd(context: KNativePointer, receiver: KNativePointer, end: KNativePointer): void { + throw new Error('Not implemented'); + } + _ClassVariableDeclaration(context: KNativePointer, classInstance: KNativePointer): KNativePointer { throw new Error('Not implemented'); } - + _IsMethodDefinition(node: KPtr): KBoolean { throw new Error('Not implemented'); } + _ProgramModuleNameConst(context: KPtr, program: KPtr): KNativePointer { + throw new Error('Not implemented'); + } + _AstNodeRangeConst(context: KNativePointer, node: KNativePointer): KNativePointer { throw new Error('CreateFunctionDecl was not overloaded by native module initialization'); } @@ -850,12 +908,85 @@ export class Es2pandaNativeModule { } _IsArrayExpression(node: KPtr): KBoolean { + throw new Error('IsArrayExpression was not overloaded by native module initialization'); + } + + _MemInitialize(): void { + throw new Error('MemInitialize was not overloaded by native module initialization'); + } + + _MemFinalize(): void { + throw new Error('MemFinalize was not overloaded by native module initialization'); + } + + _CreateGlobalContext(configPtr: KNativePointer, externalFileList: KStringArrayPtr, fileNum: KInt, + lspUsage: boolean): KNativePointer { + throw new Error('CreateGlobalContext was not overloaded by native module initialization'); + } + + _DestroyGlobalContext(contextPtr: KNativePointer): void { + throw new Error('DestroyGlobalContext was not overloaded by native module initialization'); + } + + _CreateCacheContextFromFile(configPtr: KNativePointer, filename: string, globalContext: KNativePointer, + isExternal: KBoolean): KNativePointer { + throw new Error('CreateCacheContextFromFile was not overloaded by native module initialization'); + } + + _CreateDiagnosticKind(context: KNativePointer, message: string, type: PluginDiagnosticType): KNativePointer { + throw new Error('Not implemented'); + } + + _CreateDiagnosticInfo(context: KNativePointer, kind: KNativePointer, args: string[], + argc: number, pos: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _CreateSuggestionInfo(context: KNativePointer, kind: KNativePointer, args: string[], + argc: number, substitutionCode: string, title: string, range?: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _LogDiagnostic(context: KNativePointer, kind: KNativePointer, argv: string[], argc: number, pos: KNativePointer): void { + throw new Error('Not implemented'); + } + + _LogDiagnosticWithSuggestion(context: KNativePointer, diagnosticInfo: KNativePointer, + suggestionInfo?: KNativePointer): void { throw new Error('Not implemented'); } _SetUpSoPath(soPath: string): void { throw new Error('Not implemented'); } + + _MemoryTrackerReset(context: KNativePointer): void { + throw new Error('MemoryTrackerReset was not overloaded by native module initialization'); + } + + _MemoryTrackerGetDelta(context: KNativePointer): void { + throw new Error('MemoryTrackerGetDelta was not overloaded by native module initialization'); + } + + _MemoryTrackerPrintCurrent(context: KNativePointer): void { + throw new Error('MemoryTrackerPrintCurrent was not overloaded by native module initialization'); + } + + _CallExpressionIsTrailingCallConst(context: KNativePointer, node: KNativePointer): boolean { + throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); + } + + _CreateTypeNodeFromTsType(context: KNativePointer, classInstance: KNativePointer): KNativePointer { + throw new Error('Not implemented'); + } + + _JsdocStringFromDeclaration(context: KNativePointer, decl: KNativePointer): KStringPtr { + throw new Error('Not implemented'); + } + + _ProgramSourceFilePathConst(context: KPtr, decl: KPtr): KNativePointer { + throw new Error('Not implemented'); + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/koala-wrapper/src/arkts-api/class-by-peer.ts b/koala-wrapper/src/arkts-api/class-by-peer.ts index f8d79996c6031066da0aafe1cb7535c86d6250a6..4306cff12a48d89144fcc6bff6ca23b33a8c5e09 100644 --- a/koala-wrapper/src/arkts-api/class-by-peer.ts +++ b/koala-wrapper/src/arkts-api/class-by-peer.ts @@ -19,14 +19,15 @@ import { global } from './static/global'; import { KNativePointer, nullptr } from '@koalaui/interop'; import { AstNode, UnsupportedNode } from './peers/AstNode'; -export const nodeByType = new Map([]); +type AstNodeConstructor = new (peer: KNativePointer) => AstNode; +export const nodeByType = new Map([]); const cache = new Map(); export function clearNodeCache(): void { cache.clear(); } -function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { +export function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { if (cache.has(peer)) { return cache.get(peer)!; } diff --git a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts index f8307a69bacc380cb5b8d7ddb30bcc415501bc39..1d67b4fd5c10508d3a9bad72ecc048e2c68856b2 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts @@ -13,7 +13,6 @@ * limitations under the License. */ -import { updateNodeByNode } from '../utilities/private'; import { ArrowFunctionExpression, AssignmentExpression, @@ -22,7 +21,6 @@ import { EtsScript, ExpressionStatement, FunctionDeclaration, - FunctionExpression, IfStatement, MethodDefinition, NumberLiteral, @@ -32,7 +30,6 @@ import { ETSStringLiteralType, } from '../types'; import { MemberExpression } from '../to-be-generated/MemberExpression'; -import { AstNode } from '../peers/AstNode'; import { AnnotationUsage, BinaryExpression, @@ -48,6 +45,7 @@ import { ETSTypeReferencePart, ETSUndefinedType, ETSUnionType, + FunctionExpression, FunctionSignature, Identifier, ImportSpecifier, @@ -75,13 +73,18 @@ import { Property, TemplateLiteral, ArrayExpression, + AnnotationDeclaration, TryStatement, + TSClassImplements, ForUpdateStatement, ForInStatement, ForOfStatement, + SwitchStatement, + SwitchCaseStatement, + SpreadElement, + BreakStatement, + ClassStaticBlock, } from '../../generated'; -import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; -import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; import { updateIdentifier } from '../node-utilities/Identifier'; import { updateCallExpression } from '../node-utilities/CallExpression'; import { updateExpressionStatement } from '../node-utilities/ExpressionStatement'; @@ -134,328 +137,417 @@ import { updateObjectExpression } from '../node-utilities/ObjectExpression'; import { updateProperty } from '../node-utilities/Property'; 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'; import { updateForUpdateStatement } from '../node-utilities/ForUpdateStatement'; import { updateForInStatement } from '../node-utilities/ForInStatement'; import { updateForOfStatement } from '../node-utilities/ForOfStatement'; +import { updateSwitchStatement } from '../node-utilities/SwitchStatement'; +import { updateSwitchCaseStatement } from '../node-utilities/SwitchCaseStatement'; +import { updateSpreadElement } from '../node-utilities/SpreadElement'; +import { updateBreakStatement } from '../node-utilities/BreakStatement'; +import { updateClassStaticBlock } from '../node-utilities/ClassStaticBlock'; export const factory = { - get createIdentifier() { - return Identifier.create2Identifier + get createIdentifier(): (...args: Parameters) => Identifier { + return Identifier.create2Identifier; + }, + get updateIdentifier(): (...args: Parameters) => Identifier { + return updateIdentifier; + }, + get createCallExpression(): (...args: Parameters) => CallExpression { + return CallExpression.create; }, - get updateIdentifier() { - return updateIdentifier + get updateCallExpression(): (...args: Parameters) => CallExpression { + return updateCallExpression; }, - get createCallExpression() { - return CallExpression.create + get createExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return ExpressionStatement.create; }, - get updateCallExpression() { - return updateCallExpression + get updateExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return updateExpressionStatement; }, - get createExpressionStatement() { - return ExpressionStatement.create + get createMemberExpression(): (...args: Parameters) => MemberExpression { + return MemberExpression.create; }, - get updateExpressionStatement() { - return updateExpressionStatement + get updateMemberExpression(): (...args: Parameters) => MemberExpression { + return updateMemberExpression; }, - get createMemberExpression() { - return MemberExpression.create + get createEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.createFromSource; }, - get updateMemberExpression() { - return updateMemberExpression + get updateEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.updateByStatements; }, - get createEtsScript() { - return EtsScript.createFromSource + get createFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return FunctionDeclaration.create; }, - get updateEtsScript() { - return EtsScript.updateByStatements + get updateFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return updateFunctionDeclaration; }, - get createFunctionDeclaration() { - return FunctionDeclaration.create + get createBlock(): (...args: Parameters) => BlockStatement { + return BlockStatement.createBlockStatement; }, - get updateFunctionDeclaration() { - return updateFunctionDeclaration + get updateBlock(): (...args: Parameters) => BlockStatement { + return updateBlockStatement; }, - get createBlock() { - return BlockStatement.createBlockStatement + get createArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return ArrowFunctionExpression.create; }, - get updateBlock() { - return updateBlockStatement + get updateArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return updateArrowFunctionExpression; }, - get createArrowFunction() { - return ArrowFunctionExpression.create + get createScriptFunction(): (...args: Parameters) => ScriptFunction { + return ScriptFunction.createScriptFunction; }, - get updateArrowFunction() { - return updateArrowFunctionExpression + get updateScriptFunction(): (...args: Parameters) => ScriptFunction { + return updateScriptFunction; }, - get createScriptFunction() { - return ScriptFunction.createScriptFunction + get createStringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get updateScriptFunction() { - return updateScriptFunction + get updateStringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get createStringLiteral() { - return StringLiteral.create1StringLiteral + get create1StringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get updateStringLiteral() { - return updateStringLiteral + get update1StringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get create1StringLiteral() { - return StringLiteral.create1StringLiteral + get createNumericLiteral(): (...args: Parameters) => NumberLiteral { + return NumberLiteral.create; }, - get update1StringLiteral() { - return updateStringLiteral + get updateNumericLiteral(): (...args: Parameters) => NumberLiteral { + return updateNumberLiteral; }, - get createNumericLiteral() { - return NumberLiteral.create + get createParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return ETSParameterExpression.create; }, - get updateNumericLiteral() { - return updateNumberLiteral + get updateParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return updateETSParameterExpression; }, - get createParameterDeclaration() { - return ETSParameterExpression.create + get createTypeParameter(): (...args: Parameters) => TSTypeParameter { + return TSTypeParameter.createTSTypeParameter; }, - get updateParameterDeclaration() { - return updateETSParameterExpression + get updateTypeParameter(): (...args: Parameters) => TSTypeParameter { + return updateTSTypeParameter; }, - get createTypeParameter() { - return TSTypeParameter.createTSTypeParameter + get createTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return TSTypeParameterDeclaration.createTSTypeParameterDeclaration; }, - get updateTypeParameter() { - return updateTSTypeParameter + get updateTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return updateTSTypeParameterDeclaration; }, - get createTypeParameterDeclaration() { - return TSTypeParameterDeclaration.createTSTypeParameterDeclaration + get createPrimitiveType(): ( + ...args: Parameters + ) => ETSPrimitiveType { + return ETSPrimitiveType.createETSPrimitiveType; }, - get updateTypeParameterDeclaration() { - return updateTSTypeParameterDeclaration + get updatePrimitiveType(): (...args: Parameters) => ETSPrimitiveType { + return updateETSPrimitiveType; }, - get createPrimitiveType() { - return ETSPrimitiveType.createETSPrimitiveType + get createTypeReference(): ( + ...args: Parameters + ) => ETSTypeReference { + return ETSTypeReference.createETSTypeReference; }, - get updatePrimitiveType() { - return updateETSPrimitiveType + get updateTypeReference(): (...args: Parameters) => ETSTypeReference { + return updateETSTypeReference; }, - get createTypeReference() { - return ETSTypeReference.createETSTypeReference + get createTypeReferencePart(): ( + ...args: Parameters + ) => ETSTypeReferencePart { + return ETSTypeReferencePart.createETSTypeReferencePart; }, - get updateTypeReference() { - return updateETSTypeReference + get updateTypeReferencePart(): (...args: Parameters) => ETSTypeReferencePart { + return updateETSTypeReferencePart; }, - get createTypeReferencePart() { - return ETSTypeReferencePart.createETSTypeReferencePart + get createImportDeclaration(): ( + ...args: Parameters + ) => ETSImportDeclaration { + return ETSImportDeclaration.createETSImportDeclaration; }, - get updateTypeReferencePart() { - return updateETSTypeReferencePart + get updateImportDeclaration(): (...args: Parameters) => ETSImportDeclaration { + return updateETSImportDeclaration; }, - get createImportDeclaration() { - return ETSImportDeclaration.createETSImportDeclaration + get createImportSpecifier(): ( + ...args: Parameters + ) => ImportSpecifier { + return ImportSpecifier.createImportSpecifier; }, - get updateImportDeclaration() { - return updateETSImportDeclaration + get updateImportSpecifier(): (...args: Parameters) => ImportSpecifier { + return updateImportSpecifier; }, - get createImportSpecifier() { - return ImportSpecifier.createImportSpecifier + get createVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return VariableDeclaration.create; }, - get updateImportSpecifier() { - return updateImportSpecifier + get updateVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return updateVariableDeclaration; }, - get createVariableDeclaration() { - return VariableDeclaration.create + get createVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return VariableDeclarator.create; }, - get updateVariableDeclaration() { - return updateVariableDeclaration + get updateVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return updateVariableDeclarator; }, - get createVariableDeclarator() { - return VariableDeclarator.create + get createUnionType(): (...args: Parameters) => ETSUnionType { + return ETSUnionType.createETSUnionType; }, - get updateVariableDeclarator() { - return updateVariableDeclarator + get updateUnionType(): (...args: Parameters) => ETSUnionType { + return updateETSUnionType; }, - get createUnionType() { - return ETSUnionType.createETSUnionType + get createReturnStatement(): ( + ...args: Parameters + ) => ReturnStatement { + return ReturnStatement.create1ReturnStatement; }, - get updateUnionType() { - return updateETSUnionType + get updateReturnStatement(): (...args: Parameters) => ReturnStatement { + return updateReturnStatement; }, - get createReturnStatement() { - return ReturnStatement.create1ReturnStatement + get createIfStatement(): (...args: Parameters) => IfStatement { + return IfStatement.create; }, - get updateReturnStatement() { - return updateReturnStatement + get updateIfStatement(): (...args: Parameters) => IfStatement { + return updateIfStatement; }, - get createIfStatement() { - return IfStatement.create + get createBinaryExpression(): ( + ...args: Parameters + ) => BinaryExpression { + return BinaryExpression.createBinaryExpression; }, - get updateIfStatement() { - return updateIfStatement + get updateBinaryExpression(): (...args: Parameters) => BinaryExpression { + return updateBinaryExpression; }, - get createBinaryExpression() { - return BinaryExpression.createBinaryExpression + get createClassDeclaration(): ( + ...args: Parameters + ) => ClassDeclaration { + return ClassDeclaration.createClassDeclaration; }, - get updateBinaryExpression() { - return updateBinaryExpression + get updateClassDeclaration(): (...args: Parameters) => ClassDeclaration { + return updateClassDeclaration; }, - get createClassDeclaration() { - return ClassDeclaration.createClassDeclaration + get createStructDeclaration(): (...args: Parameters) => StructDeclaration { + return StructDeclaration.create; }, - get updateClassDeclaration() { - return updateClassDeclaration + get updateStructDeclaration(): (...args: Parameters) => StructDeclaration { + return updateStructDeclaration; }, - get createStructDeclaration() { - return StructDeclaration.create + get createClassDefinition(): ( + ...args: Parameters + ) => ClassDefinition { + return ClassDefinition.createClassDefinition; }, - get updateStructDeclaration() { - return updateStructDeclaration + get updateClassDefinition(): (...args: Parameters) => ClassDefinition { + return updateClassDefinition; }, - get createClassDefinition() { - return ClassDefinition.createClassDefinition + get createClassProperty(): (...args: Parameters) => ClassProperty { + return ClassProperty.createClassProperty; }, - get updateClassDefinition() { - return updateClassDefinition + get updateClassProperty(): (...args: Parameters) => ClassProperty { + return updateClassProperty; }, - get createClassProperty() { - return ClassProperty.createClassProperty + get createFunctionType(): (...args: Parameters) => ETSFunctionType { + return ETSFunctionType.createETSFunctionType; }, - get updateClassProperty() { - return updateClassProperty + get updateFunctionType(): (...args: Parameters) => ETSFunctionType { + return updateETSFunctionType; }, - get createFunctionType() { - return ETSFunctionType.createETSFunctionType + get createFunctionExpression(): ( + ...args: Parameters + ) => FunctionExpression { + return FunctionExpression.createFunctionExpression; }, - get updateFunctionType() { - return updateETSFunctionType + get updateFunctionExpression(): (...args: Parameters) => FunctionExpression { + return updateFunctionExpression; }, - get createFunctionExpression() { - return FunctionExpression.create + get createMethodDefinition(): (...args: Parameters) => MethodDefinition { + return MethodDefinition.create; }, - get updateFunctionExpression() { - return updateFunctionExpression + get updateMethodDefinition(): (...args: Parameters) => MethodDefinition { + return updateMethodDefinition; }, - get createMethodDefinition() { - return MethodDefinition.create + get createSuperExpression(): ( + ...args: Parameters + ) => SuperExpression { + return SuperExpression.createSuperExpression; }, - get updateMethodDefinition() { - return updateMethodDefinition + get updateSuperExpression(): (...args: Parameters) => SuperExpression { + return updateSuperExpression; }, - get createSuperExpression() { - return SuperExpression.createSuperExpression + get createTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return TSTypeParameterInstantiation.createTSTypeParameterInstantiation; }, - get updateSuperExpression() { - return updateSuperExpression + get updateTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return updateTSTypeParameterInstantiation; }, - get createTSTypeParameterInstantiation() { - return TSTypeParameterInstantiation.createTSTypeParameterInstantiation + get createInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return TSInterfaceDeclaration.createTSInterfaceDeclaration; }, - get updateTSTypeParameterInstantiation() { - return updateTSTypeParameterInstantiation + get updateInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return updateTSInterfaceDeclaration; }, - get createInterfaceDeclaration() { - return TSInterfaceDeclaration.createTSInterfaceDeclaration + get createInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return TSInterfaceBody.createTSInterfaceBody; }, - get updateInterfaceDeclaration() { - return updateTSInterfaceDeclaration + get updateInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return updateTSInterfaceBody; }, - get createInterfaceBody() { - return TSInterfaceBody.createTSInterfaceBody + get createUndefinedLiteral(): ( + ...args: Parameters + ) => UndefinedLiteral { + return UndefinedLiteral.createUndefinedLiteral; }, - get updateInterfaceBody() { - return updateTSInterfaceBody + get updateUndefinedLiteral(): (...args: Parameters) => UndefinedLiteral { + return updateUndefinedLiteral; }, - get createUndefinedLiteral() { - return UndefinedLiteral.createUndefinedLiteral + get createAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return AnnotationDeclaration.create1AnnotationDeclaration; }, - get updateUndefinedLiteral() { - return updateUndefinedLiteral + get updateAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return updateAnnotationDeclaration; }, - get createAnnotationUsage() { - return AnnotationUsage.createAnnotationUsage + get createAnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { + return AnnotationUsage.createAnnotationUsage; }, - get updateAnnotationUsage() { - return updateAnnotationUsage + get updateAnnotationUsage(): (...args: Parameters) => AnnotationUsage { + return updateAnnotationUsage; }, - get create1AnnotationUsage(): (...args: Parameters) => AnnotationUsage { + get create1AnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { return AnnotationUsage.create1AnnotationUsage; }, get update1AnnotationUsage(): (...args: Parameters) => AnnotationUsage { return update1AnnotationUsage; }, - get createAssignmentExpression() { - return AssignmentExpression.create + get createAssignmentExpression(): ( + ...args: Parameters + ) => AssignmentExpression { + return AssignmentExpression.create; }, - get updateAssignmentExpression() { - return updateAssignmentExpression + get updateAssignmentExpression(): (...args: Parameters) => AssignmentExpression { + return updateAssignmentExpression; }, - get createETSUndefinedType() { - return ETSUndefinedType.createETSUndefinedType + get createETSUndefinedType(): ( + ...args: Parameters + ) => ETSUndefinedType { + return ETSUndefinedType.createETSUndefinedType; }, - get updateETSUndefinedType() { - return updateETSUndefinedType + get updateETSUndefinedType(): (...args: Parameters) => ETSUndefinedType { + return updateETSUndefinedType; }, - get createFunctionSignature() { - return FunctionSignature.createFunctionSignature + get createFunctionSignature(): ( + ...args: Parameters + ) => FunctionSignature { + return FunctionSignature.createFunctionSignature; }, - get createConditionalExpression() { - return ConditionalExpression.createConditionalExpression + get createConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return ConditionalExpression.createConditionalExpression; }, - get updateConditionalExpression() { - return updateConditionalExpression + get updateConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return updateConditionalExpression; }, - get createTSAsExpression() { - return TSAsExpression.createTSAsExpression + get createTSAsExpression(): (...args: Parameters) => TSAsExpression { + return TSAsExpression.createTSAsExpression; }, - get updateTSAsExpression() { - return updateTSAsExpression + get updateTSAsExpression(): (...args: Parameters) => TSAsExpression { + return updateTSAsExpression; }, - get createThisExpression() { - return ThisExpression.createThisExpression + get createThisExpression(): (...args: Parameters) => ThisExpression { + return ThisExpression.createThisExpression; }, - get updateThisExpression() { - return updateThisExpression + get updateThisExpression(): (...args: Parameters) => ThisExpression { + return updateThisExpression; }, - get createTSTypeAliasDeclaration() { - return TSTypeAliasDeclaration.createTSTypeAliasDeclaration + get createTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return TSTypeAliasDeclaration.createTSTypeAliasDeclaration; }, - get updateTSTypeAliasDeclaration() { - return updateTSTypeAliasDeclaration + get updateTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return updateTSTypeAliasDeclaration; }, - get createTSNonNullExpression() { - return TSNonNullExpression.createTSNonNullExpression + get createTSNonNullExpression(): ( + ...args: Parameters + ) => TSNonNullExpression { + return TSNonNullExpression.createTSNonNullExpression; }, - get updateTSNonNullExpression() { - return updateTSNonNullExpression + get updateTSNonNullExpression(): (...args: Parameters) => TSNonNullExpression { + return updateTSNonNullExpression; }, - get createChainExpression() { - return ChainExpression.createChainExpression + get createChainExpression(): ( + ...args: Parameters + ) => ChainExpression { + return ChainExpression.createChainExpression; }, - get updateChainExpression() { - return updateChainExpression + get updateChainExpression(): (...args: Parameters) => ChainExpression { + return updateChainExpression; }, - get createBlockExpression() { - return BlockExpression.createBlockExpression + get createBlockExpression(): ( + ...args: Parameters + ) => BlockExpression { + return BlockExpression.createBlockExpression; }, - get updateBlockExpression() { - return updateBlockExpression + get updateBlockExpression(): (...args: Parameters) => BlockExpression { + return updateBlockExpression; }, - get createNullLiteral() { - return NullLiteral.createNullLiteral + get createNullLiteral(): (...args: Parameters) => NullLiteral { + return NullLiteral.createNullLiteral; }, - get updateNullLiteral() { - return updateNullLiteral + get updateNullLiteral(): (...args: Parameters) => NullLiteral { + return updateNullLiteral; }, - get createETSNewClassInstanceExpression() { - return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression + get createETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression; }, - get updateETSNewClassInstanceExpression() { - return updateETSNewClassInstanceExpression + get updateETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return updateETSNewClassInstanceExpression; }, - get createETSStringLiteralType() { + get createETSStringLiteralType(): ( + ...args: Parameters + ) => ETSStringLiteralType { return ETSStringLiteralType.create; }, get createBooleanLiteral(): (...args: Parameters) => BooleanLiteral { return BooleanLiteral.createBooleanLiteral; }, - get createObjectExpression(): (...args: Parameters) => ObjectExpression { + get createObjectExpression(): ( + ...args: Parameters + ) => ObjectExpression { return ObjectExpression.createObjectExpression; }, get updateObjectExpression(): (...args: Parameters) => ObjectExpression { @@ -467,13 +559,17 @@ export const factory = { get updateProperty(): (...args: Parameters) => Property { return updateProperty; }, - get createTemplateLiteral(): (...args: Parameters) => TemplateLiteral { + get createTemplateLiteral(): ( + ...args: Parameters + ) => TemplateLiteral { return TemplateLiteral.createTemplateLiteral; }, get updateTemplateLiteral(): (...args: Parameters) => TemplateLiteral { return updateTemplateLiteral; }, - get createArrayExpression(): (...args: Parameters) => ArrayExpression { + get createArrayExpression(): ( + ...args: Parameters + ) => ArrayExpression { return ArrayExpression.createArrayExpression; }, get updateArrayExpression(): (...args: Parameters) => ArrayExpression { @@ -485,6 +581,14 @@ 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; + }, get createForUpdateStatement(): ( ...args: Parameters ) => ForUpdateStatement { @@ -505,8 +609,44 @@ export const factory = { get updateForOfStatement(): (...args: Parameters) => ForOfStatement { return updateForOfStatement; }, + get createSwitchStatement(): ( + ...args: Parameters + ) => SwitchStatement { + return SwitchStatement.createSwitchStatement; + }, + get updateSwitchStatement(): (...args: Parameters) => SwitchStatement { + return updateSwitchStatement; + }, + get createSwitchCaseStatement(): ( + ...args: Parameters + ) => SwitchCaseStatement { + return SwitchCaseStatement.createSwitchCaseStatement; + }, + get updateSwitchCaseStatement(): (...args: Parameters) => SwitchCaseStatement { + return updateSwitchCaseStatement; + }, + get createSpreadElement(): (...args: Parameters) => SpreadElement { + return SpreadElement.createSpreadElement; + }, + get updateSpreadElement(): (...args: Parameters) => SpreadElement { + return updateSpreadElement; + }, + get createBreakStatement(): (...args: Parameters) => BreakStatement { + return BreakStatement.createBreakStatement; + }, + get updateBreakStatement(): (...args: Parameters) => BreakStatement { + return updateBreakStatement; + }, + get createClassStaticBlock(): ( + ...args: Parameters + ) => ClassStaticBlock { + return ClassStaticBlock.createClassStaticBlock; + }, + get updateClassStaticBlock(): (...args: Parameters) => ClassStaticBlock { + return updateClassStaticBlock; + }, /** @deprecated */ createTypeParameter1_(name: Identifier, constraint?: TypeNode, defaultType?: TypeNode) { - return TSTypeParameter.createTSTypeParameter(Identifier.create1Identifier(name.name), constraint, defaultType) + return TSTypeParameter.createTSTypeParameter(Identifier.create1Identifier(name.name), constraint, defaultType); }, -} +}; diff --git a/koala-wrapper/src/arkts-api/factory/nodeTests.ts b/koala-wrapper/src/arkts-api/factory/nodeTests.ts index 62c02275a505a51ae282d041066c946d32d22dd1..6b865346918d98f953647a9f0aca4297a319cdb0 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeTests.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeTests.ts @@ -21,10 +21,8 @@ import { EtsScript, ExpressionStatement, FunctionDeclaration, - FunctionExpression, IfStatement, MethodDefinition, - // ScriptFunction, StructDeclaration, VariableDeclaration, VariableDeclarator, @@ -35,15 +33,15 @@ import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" export function isCallExpression(node: AstNode): node is CallExpression { - return node instanceof CallExpression + return node instanceof CallExpression; } export function isMemberExpression(node: AstNode): node is MemberExpression { - return node instanceof MemberExpression + return node instanceof MemberExpression; } export function isFunctionDeclaration(node: AstNode): node is FunctionDeclaration { - return node instanceof FunctionDeclaration + return node instanceof FunctionDeclaration; } export function isMethodDefinition(node: AstNode): node is MethodDefinition { @@ -51,43 +49,35 @@ export function isMethodDefinition(node: AstNode): node is MethodDefinition { } export function isEtsScript(node: AstNode): node is EtsScript { - return node instanceof EtsScript + return node instanceof EtsScript; } export function isExpressionStatement(node: AstNode): node is ExpressionStatement { - return node instanceof ExpressionStatement + return node instanceof ExpressionStatement; } export function isArrowFunctionExpression(node: AstNode): node is ArrowFunctionExpression { - return node instanceof ArrowFunctionExpression + return node instanceof ArrowFunctionExpression; } export function isStructDeclaration(node: AstNode): node is StructDeclaration { - return node instanceof StructDeclaration -} - -export function isFunctionExpression(node: AstNode): node is FunctionExpression { - return node instanceof FunctionExpression + return node instanceof StructDeclaration; } export function isEtsParameterExpression(node: AstNode): node is ETSParameterExpression { - return node instanceof ETSParameterExpression + return node instanceof ETSParameterExpression; } export function isVariableDeclaration(node: AstNode): node is VariableDeclaration { - return node instanceof VariableDeclaration + return node instanceof VariableDeclaration; } -// export function isScriptFunction(node: AstNode): node is ScriptFunction { -// return node instanceof ScriptFunction -// } - export function isIfStatement(node: AstNode): node is IfStatement { - return node instanceof IfStatement + return node instanceof IfStatement; } export function isVariableDeclarator(node: AstNode): node is VariableDeclarator { - return node instanceof VariableDeclarator + return node instanceof VariableDeclarator; } export function isAssignmentExpression(node: AstNode): node is AssignmentExpression { diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index 5976735bf64295c7b28a266d4c0f8291dfac102c..647c8e2ee7c2bfb863536a8d90c1c86cf0067e6a 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -15,6 +15,7 @@ export * from '../Es2pandaEnums'; export * from '../generated/Es2pandaEnums'; +export * from '../generated/peers/AnnotationDeclaration'; export * from '../generated/peers/AnnotationUsage'; export * from '../generated/peers/BlockStatement'; export * from '../generated/peers/ETSPrimitiveType'; @@ -60,12 +61,23 @@ export * from '../generated/peers/TSClassImplements'; export * from '../generated/peers/BooleanLiteral'; export * from '../generated/peers/TSArrayType'; export * from '../generated/peers/ArrayExpression'; +export * from '../generated/peers/TryStatement'; export * from '../generated/peers/ETSNullType'; +export * from '../generated/peers/ETSTuple'; +export * from '../generated/peers/ImportDeclaration'; +export * from '../generated/peers/WhileStatement'; +export * from '../generated/peers/BreakStatement'; +export * from '../generated/peers/ContinueStatement'; +export * from '../generated/peers/SwitchCaseStatement'; +export * from '../generated/peers/SwitchStatement'; +export * from '../generated/peers/ClassStaticBlock'; +export * from '../generated/peers/FunctionExpression'; export * from './types'; export * from './utilities/private'; export * from './utilities/public'; export * from './utilities/performance'; +export * from './utilities/nodeCache'; export * from './factory/nodeFactory'; export * from './factory/nodeTests'; export * from './visitor'; @@ -78,6 +90,10 @@ export * from './peers/Program'; export * from './peers/ImportPathManager'; export * from './peers/SourcePosition'; export * from './peers/SourceRange'; +export * from './peers/Diagnostic'; +export * from './peers/DiagnosticInfo'; +export * from './peers/DiagnosticKind'; +export * from './peers/SuggestionInfo'; export * from './to-be-generated/MemberExpression'; export * from './static/globalUtils'; export { global as arktsGlobal } from './static/global'; diff --git a/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts new file mode 100644 index 0000000000000000000000000000000000000000..345d95f213023ba56c28a1b1db72b9f222ec014e --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/AnnotationDeclaration.ts @@ -0,0 +1,32 @@ +/* + * 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 { AnnotationDeclaration, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { AstNode } from '../peers/AstNode'; + +export function updateAnnotationDeclaration( + original: AnnotationDeclaration, + expr: Expression | undefined, + properties: readonly AstNode[] +): AnnotationDeclaration { + if (isSameNativeObject(expr, original.expr) && isSameNativeObject(properties, original.properties)) { + return original; + } + + const update = updateThenAttach(AnnotationDeclaration.update1AnnotationDeclaration, attachModifiers); + return update(original, expr, properties); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts index 9ad9e11afbee51c6175dd54396dc075d8c039cc9..a787698d718980e62d0294e41933ca4ac8e94e6d 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts @@ -16,6 +16,7 @@ import { ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { ArrowFunctionExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateArrowFunctionExpression( @@ -31,5 +32,9 @@ export function updateArrowFunctionExpression( attachModifiers, (node: ArrowFunctionExpression, original: ArrowFunctionExpression) => node.setAnnotations(original.annotations) ); - return update(original, func); + const newNode = update(original, func); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..03ed1b141f0a897d4d9cf76cff36a490aac3f003 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/BreakStatement.ts @@ -0,0 +1,27 @@ +/* + * 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 { BreakStatement } from '../../generated'; +import { NodeCache } from '../utilities/nodeCache'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateBreakStatement(original: BreakStatement): BreakStatement { + const update = updateThenAttach(BreakStatement.updateBreakStatement, attachModifiers); + const newNode = update(original); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts index d1489441acb897ed397d6a1d0a89098b216fd891..843798827d52fdba2515bca58485b23df99560b6 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts @@ -17,6 +17,7 @@ import { TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { CallExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateCallExpression( @@ -41,5 +42,9 @@ export function updateCallExpression( (node: CallExpression, original: CallExpression) => !!original.trailingBlock ? node.setTralingBlock(original.trailingBlock) : node ); - return update(original, expression, typeArguments, args, isOptional, trailingComma); + const newNode = update(original, expression, typeArguments, args, isOptional, trailingComma); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts index 1e9ae0ffb4e7634a52eb5a304e477c238aa786b9..793c00935a0982a76273a1e348f91f55ddd80f99 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassDefinition.ts @@ -27,35 +27,38 @@ import { MethodDefinition } from '../types'; import { updateThenAttach } from '../utilities/private'; import { Es2pandaClassDefinitionModifiers, Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; import { classDefinitionFlags } from '../utilities/public'; +import { Es2pandaLanguage } from '..'; export function updateClassDefinition( original: ClassDefinition, ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, - _implements: readonly TSClassImplements[], + classImplements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, - flags: Es2pandaModifierFlags + flags: Es2pandaModifierFlags, + lang?: Es2pandaLanguage ): ClassDefinition { if ( isSameNativeObject(ident, original.ident) && isSameNativeObject(typeParams, original.typeParams) && isSameNativeObject(superTypeParams, original.superTypeParams) && - isSameNativeObject(_implements, original.implements) && + isSameNativeObject(classImplements, original.implements) && isSameNativeObject(superClass, original.super) && isSameNativeObject(body, original.body) && isSameNativeObject(modifiers, original.modifiers) && - isSameNativeObject(flags, classDefinitionFlags(original)) + isSameNativeObject(flags, classDefinitionFlags(original)) && + (!lang || isSameNativeObject(lang, original.lang)) /* TODO: no getter for ctor */ ) { return original; } const update = updateThenAttach( - ClassDefinition.updateClassDefinition, + ClassDefinition.update3ClassDefinition, (node: ClassDefinition, original: ClassDefinition) => node.setAnnotations(original.annotations) ); return update( @@ -63,11 +66,13 @@ export function updateClassDefinition( ident, typeParams, superTypeParams, - _implements, + classImplements, undefined, superClass, body, modifiers, - flags + flags, + lang ?? original.lang ); } + \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts index 06450c0cb4c32b74ed30eeb105eb7971ae6ea60d..1d31c28588a253d92fc5735de160b8694336b1ee 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts @@ -18,6 +18,7 @@ import { isSameNativeObject } from '../peers/ArktsObject'; import { updateThenAttach } from '../utilities/private'; import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateClassProperty( original: ClassProperty, @@ -47,5 +48,9 @@ export function updateClassProperty( return node; } ); - return update(original, key, value, typeAnnotation, modifiers, isComputed); + const newNode = update(original, key, value, typeAnnotation, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a49d2274a17a34a3a7814241078d60118066fba --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassStaticBlock.ts @@ -0,0 +1,28 @@ +/* + * 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 { ClassStaticBlock, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateClassStaticBlock(original: ClassStaticBlock, value?: Expression): ClassStaticBlock { + if (isSameNativeObject(value, original.value)) { + return original; + } + + const update = updateThenAttach(ClassStaticBlock.updateClassStaticBlock, attachModifiers); + const newNode = update(original, value); + return newNode; +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts index a0e5220406037d90b62668942eecb5663266da94..c4e44197444b5f24678212b0566cafab7561cd3c 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts @@ -17,6 +17,7 @@ import { ETSFunctionType, FunctionSignature } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaScriptFunctionFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateETSFunctionType( original: ETSFunctionType, @@ -38,5 +39,9 @@ export function updateETSFunctionType( attachModifiers, (node: ETSFunctionType, original: ETSFunctionType) => node.setAnnotations(original.annotations) ); - return update(original, signature, funcFlags); + const newNode = update(original, signature, funcFlags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts index b9e8f0514f159eab38c00d6168e002a7ca92cea4..23923cd754012a58b9f5a01fc4074da05cbc0aa3 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts @@ -17,6 +17,7 @@ import { Identifier } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { ETSParameterExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSParameterExpression( @@ -37,7 +38,11 @@ export function updateETSParameterExpression( (node: ETSParameterExpression, original: ETSParameterExpression) => { node.annotations = original.annotations; return node; - } + }, ); - return update(original, identifier, initializer); + const newNode = update(original, identifier, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts index c8dc20720dde2cb9b61918cbb90cabe459efa0a8..07ab40282d2de1e6d7f114ec389d334a0e3bd3b4 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts @@ -15,6 +15,7 @@ import { ETSUnionType, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSUnionType(original: ETSUnionType, types: readonly TypeNode[]): ETSUnionType { @@ -23,5 +24,9 @@ export function updateETSUnionType(original: ETSUnionType, types: readonly TypeN } const update = updateThenAttach(ETSUnionType.updateETSUnionType, attachModifiers); - return update(original, types); + const newNode = update(original, types); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts index ba213580140feac8c5b73169a164599e5714c915..b601e9f469e5e8cae44fab50a85334e1e7e9d500 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/FunctionExpression.ts @@ -13,16 +13,15 @@ * limitations under the License. */ -import { ScriptFunction } from '../../generated'; +import { ScriptFunction, FunctionExpression } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; -import { FunctionExpression } from '../types'; import { attachModifiers, updateThenAttach } from '../utilities/private'; -export function updateFunctionExpression(original: FunctionExpression, expression: ScriptFunction): FunctionExpression { - if (isSameNativeObject(expression, original.scriptFunction)) { +export function updateFunctionExpression(original: FunctionExpression, func?: ScriptFunction): FunctionExpression { + if (isSameNativeObject(func, original.function)) { return original; } - const update = updateThenAttach(FunctionExpression.update, attachModifiers); - return update(original, expression); + const update = updateThenAttach(FunctionExpression.updateFunctionExpression, attachModifiers); + return update(original, func); } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts index f31d25fc675f6c4a08c904785c28b83017638deb..3a1fff7830dadc55cc20183e6d1f8eb83f1705d5 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts @@ -15,6 +15,7 @@ import { Identifier, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateIdentifier(original: Identifier, name: string, typeAnnotation?: TypeNode): Identifier { @@ -23,5 +24,9 @@ export function updateIdentifier(original: Identifier, name: string, typeAnnotat } const update = updateThenAttach(Identifier.update2Identifier, attachModifiers); - return update(original, name, typeAnnotation); + const newNode = update(original, name, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts index 42006a30098d4b5fbba20cf9f5e9c926afa36236..6318a0c84fd2c28d360abd7169b37f8eaec4c74b 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts @@ -20,6 +20,7 @@ import { MethodDefinition } from '../types'; import { updateThenAttach } from '../utilities/private'; import { Es2pandaMethodDefinitionKind } from '../../generated/Es2pandaEnums'; import { ScriptFunction } from '../../generated'; +import { NodeCache } from '../utilities/nodeCache'; export function updateMethodDefinition( original: MethodDefinition, @@ -42,5 +43,9 @@ export function updateMethodDefinition( const update = updateThenAttach(MethodDefinition.update, (node: MethodDefinition, original: MethodDefinition) => node.setOverloads(original.overloads) ); - return update(original, kind, key, value, modifiers, isComputed); + const newNode = update(original, kind, key, value, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Property.ts b/koala-wrapper/src/arkts-api/node-utilities/Property.ts index 4b0a33c5b4d37c7fc33e958abe99d80805920c9d..fdb1443c593831eb1158f28398b4ed6484af27e5 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Property.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Property.ts @@ -15,6 +15,7 @@ import { Expression, Property } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateProperty(original: Property, key?: Expression, value?: Expression): Property { @@ -23,5 +24,10 @@ export function updateProperty(original: Property, key?: Expression, value?: Exp } const update = updateThenAttach(Property.updateProperty, attachModifiers); - return update(original, key, value); + const newNode = update(original, key, value); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; + } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts index 9fc05a3715761588fe7d4faca9a006539d1bd2db..984e0aa5d9afb1218ef7d8c0d1377d290b9a33dd 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts @@ -15,6 +15,7 @@ import { Expression, ReturnStatement } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateReturnStatement(original: ReturnStatement, argument?: Expression): ReturnStatement { @@ -23,5 +24,9 @@ export function updateReturnStatement(original: ReturnStatement, argument?: Expr } const update = updateThenAttach(ReturnStatement.update1ReturnStatement, attachModifiers); - return update(original, argument); + const newNode = update(original, argument); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts index 745a5398c7425ecc77cd306a047cca69caa362d7..16e30342a1e529803fdfddd818e5fcd358eb05dc 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts @@ -16,6 +16,7 @@ import { FunctionSignature, ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; import { updateThenAttach } from '../utilities/private'; export function updateScriptFunction( @@ -42,5 +43,9 @@ export function updateScriptFunction( (node: ScriptFunction, original: ScriptFunction) => (!!original.id ? node.setIdent(original.id) : node), (node: ScriptFunction, original: ScriptFunction) => node.setAnnotations(original.annotations) ); - return update(original, databody, datasignature, datafuncFlags, dataflags); + const newNode = update(original, databody, datasignature, datafuncFlags, dataflags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts b/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts new file mode 100644 index 0000000000000000000000000000000000000000..f8e24176deed5b2fda83d523f7a434a7bebe2b0d --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SpreadElement.ts @@ -0,0 +1,34 @@ +/* + * 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, SpreadElement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +import { global } from '../static/global'; + +export function updateSpreadElement( + original: SpreadElement, + nodeType: Es2pandaAstNodeType, + argument?: Expression +): SpreadElement { + const originalNodeType = global.generatedEs2panda._AstNodeTypeConst(global.context, original.peer); + if (isSameNativeObject(argument, original.argument) && isSameNativeObject(nodeType, originalNodeType)) { + return original; + } + + const update = updateThenAttach(SpreadElement.updateSpreadElement, attachModifiers); + return update(original, nodeType, argument); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e3e528ef59f0824c4794dd26d1953a69d148eb8 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SwitchCaseStatement.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, Statement, SwitchCaseStatement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateSwitchCaseStatement( + original: SwitchCaseStatement, + test: Expression | undefined, + consequent: readonly Statement[] +): SwitchCaseStatement { + if (isSameNativeObject(test, original.test) && isSameNativeObject(consequent, original.consequent)) { + return original; + } + + const update = updateThenAttach(SwitchCaseStatement.updateSwitchCaseStatement, attachModifiers); + return update(original, test, consequent); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf601ee8a50322f616f78d99072fa2a76ef9ffe7 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/SwitchStatement.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, SwitchCaseStatement, SwitchStatement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateSwitchStatement( + original: SwitchStatement, + discriminant: Expression | undefined, + cases: readonly SwitchCaseStatement[] +): SwitchStatement { + if (isSameNativeObject(discriminant, original.discriminant) && isSameNativeObject(cases, original.cases)) { + return original; + } + + const update = updateThenAttach(SwitchStatement.updateSwitchStatement, attachModifiers); + return update(original, discriminant, cases); +} 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 0000000000000000000000000000000000000000..1e51e9c444240d1f69dc6a327219d4d9e454f800 --- /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/arkts-api/node-utilities/TSInterfaceDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts index d268c5d82c82ebaf1e94f106c367a684bd55f491..1d607b245d1ccb579317895894b35dafe5562bcd 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/TSInterfaceDeclaration.ts @@ -38,6 +38,10 @@ export function updateTSInterfaceDeclaration( return original; } - const update = updateThenAttach(TSInterfaceDeclaration.updateTSInterfaceDeclaration, attachModifiers); + const update = updateThenAttach( + TSInterfaceDeclaration.updateTSInterfaceDeclaration, + attachModifiers, + (node: TSInterfaceDeclaration, original: TSInterfaceDeclaration) => node.setAnnotations(original.annotations) + ); return update(original, _extends, id, typeParams, body, isStatic, isExternal); } diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts index 3531ee3a95fbd0171fc0afcab621111bb21b58f9..a1d47e0e145ef76deb0721db001f0ebc96965870 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts @@ -15,6 +15,7 @@ import { Identifier, TSTypeAliasDeclaration, TSTypeParameterDeclaration, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateTSTypeAliasDeclaration( @@ -36,5 +37,9 @@ export function updateTSTypeAliasDeclaration( attachModifiers, (node: TSTypeAliasDeclaration, original: TSTypeAliasDeclaration) => node.setAnnotations(original.annotations) ); - return update(original, id, typeParams, typeAnnotation); + const newNode = update(original, id, typeParams, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts index 52c002fd3096c1edca878f99ffff2f4adb6f5861..1f1739da249207a9650952e08f4d741ae977dafd 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts @@ -19,6 +19,7 @@ import { VariableDeclarator } from '../types'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaVariableDeclaratorFlag } from '../../generated/Es2pandaEnums'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; export function updateVariableDeclarator( original: VariableDeclarator, @@ -35,5 +36,9 @@ export function updateVariableDeclarator( } const update = updateThenAttach(VariableDeclarator.update, attachModifiers); - return update(original, flag, name, initializer); + const newNode = update(original, flag, name, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/peers/AstNode.ts b/koala-wrapper/src/arkts-api/peers/AstNode.ts index 62a0b78d257036fa30639285875f363eddc9535e..6d49f5da998e17e29a875d811c038959fc96cb90 100644 --- a/koala-wrapper/src/arkts-api/peers/AstNode.ts +++ b/koala-wrapper/src/arkts-api/peers/AstNode.ts @@ -13,122 +13,149 @@ * limitations under the License. */ -import { isNullPtr, KInt, KNativePointer as KPtr, KNativePointer, nullptr } from "@koalaui/interop" -import { global } from "../static/global" -import { allFlags, nodeType, unpackNodeArray, unpackNonNullableNode, unpackString } from "../utilities/private" -import { throwError } from "../../utils" -import { Es2pandaModifierFlags } from "../../generated/Es2pandaEnums" -import { ArktsObject } from "./ArktsObject" -import { Es2pandaAstNodeType } from "../../Es2pandaEnums" +import { isNullPtr, KInt, KNativePointer as KPtr, KNativePointer, nullptr } from '@koalaui/interop'; +import { global } from '../static/global'; +import { allFlags, unpackNode, unpackNodeArray, unpackNonNullableNode, unpackString } from '../utilities/private'; +import { throwError } from '../../utils'; +import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { ArktsObject } from './ArktsObject'; +import { SourcePosition } from './SourcePosition'; export abstract class AstNode extends ArktsObject { protected constructor(peer: KNativePointer) { if (isNullPtr(peer)) { - throwError(`attempted to create AstNode from nullptr`) + throwError(`attempted to create AstNode from nullptr`); } - super(peer) - this.updateChildren() + super(peer); + this.updateChildren(); } public get originalPeer(): KNativePointer { - const result = global.generatedEs2panda._AstNodeOriginalNodeConst(global.context, this.peer) + const result = global.generatedEs2panda._AstNodeOriginalNodeConst(global.context, this.peer); if (result === nullptr) { - this.originalPeer = this.peer - return this.peer + this.originalPeer = this.peer; + return this.peer; } - return result + return result; } public set originalPeer(peer: KNativePointer) { - global.generatedEs2panda._AstNodeSetOriginalNode(global.context, this.peer, peer) + global.generatedEs2panda._AstNodeSetOriginalNode(global.context, this.peer, peer); } public getChildren(): readonly AstNode[] { - return unpackNodeArray(global.es2panda._AstNodeChildren(global.context, this.peer)) + return unpackNodeArray(global.es2panda._AstNodeChildren(global.context, this.peer)); } public getSubtree(): readonly AstNode[] { return this.getChildren().reduce( (prev: readonly AstNode[], curr) => { - return prev.concat(curr.getSubtree()) + return prev.concat(curr.getSubtree()); }, [this] - ) + ); } public updateChildren(): void { if (this.peer === nullptr) { - throwError('updateChildren called on NULLPTR') + throwError('updateChildren called on NULLPTR'); } - global.es2panda._AstNodeUpdateChildren(global.context, this.peer) + global.es2panda._AstNodeUpdateChildren(global.context, this.peer); } public updateModifiers(modifierFlags: KInt | undefined): this { - global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags) - global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, modifierFlags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE) - return this + global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags); + global.generatedEs2panda._AstNodeAddModifier( + global.context, + this.peer, + modifierFlags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + return this; } public dump(indentation: number = 0): string { - const children = this.getChildren() - .map((it) => it.dump(indentation + 1)) - const msg = - `${indentation}_` - + ` ` - + this.dumpMessage() - return "> " + " ".repeat(4 * indentation) + msg + "\n" + children.join("") + const children = this.getChildren().map((it) => it.dump(indentation + 1)); + const msg = `${indentation}_ ${this.dumpMessage()}`; + return '> ' + ' '.repeat(4 * indentation) + msg + '\n' + children.join(''); } protected dumpMessage(): string { - return `` + return ``; } public dumpJson(): string { - return unpackString(global.generatedEs2panda._AstNodeDumpJSONConst(global.context, this.peer)) + return unpackString(global.generatedEs2panda._AstNodeDumpJSONConst(global.context, this.peer)); } public dumpSrc(): string { - return unpackString(global.generatedEs2panda._AstNodeDumpEtsSrcConst(global.context, this.peer)) + return unpackString(global.generatedEs2panda._AstNodeDumpEtsSrcConst(global.context, this.peer)); } public dumpModifiers(): string { - return unpackString(global.es2panda._AstNodeDumpModifiers(global.context, this.peer)) + return unpackString(global.es2panda._AstNodeDumpModifiers(global.context, this.peer)); } public clone(): this { - return unpackNonNullableNode(global.generatedEs2panda._AstNodeClone(global.context, this.peer, this.parent.peer)); + const clonedNode = unpackNonNullableNode( + global.generatedEs2panda._AstNodeClone(global.context, this.peer, this.parent?.peer ?? nullptr) + ); + clonedNode.parent = undefined; + return clonedNode as this; } - public get parent(): AstNode { - const parent = global.generatedEs2panda._AstNodeParent(global.context, this.peer) - if (parent === nullptr) { - throwError(`no parent`) - } - return unpackNonNullableNode(parent) + public get parent(): AstNode | undefined { + const parent = global.generatedEs2panda._AstNodeParent(global.context, this.peer); + return unpackNode(parent); } - public set parent(node: AstNode) { - global.generatedEs2panda._AstNodeSetParent(global.context, this.peer, node.peer) + public set parent(node: AstNode | undefined) { + global.generatedEs2panda._AstNodeSetParent(global.context, this.peer, node?.peer ?? nullptr); } public get modifiers(): KInt { - return global.generatedEs2panda._AstNodeModifiers(global.context, this.peer) + return global.generatedEs2panda._AstNodeModifiers(global.context, this.peer); } public set modifiers(flags: KInt | undefined) { - global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags) - global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, flags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE) + global.generatedEs2panda._AstNodeClearModifier(global.context, this.peer, allFlags); + global.generatedEs2panda._AstNodeAddModifier( + global.context, + this.peer, + flags ?? Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + } + + public get isExport(): boolean { + return global.generatedEs2panda._AstNodeIsExportedConst(global.context, this.peer); + } + + public get isDefaultExport(): boolean { + return global.generatedEs2panda._AstNodeIsDefaultExportedConst(global.context, this.peer); } public get isStatic(): boolean { - return global.generatedEs2panda._AstNodeIsStaticConst(global.context, this.peer) + return global.generatedEs2panda._AstNodeIsStaticConst(global.context, this.peer); + } + + public get startPosition(): SourcePosition { + return new SourcePosition(global.es2panda._AstNodeStartConst(global.context, this.peer)); + } + + public set startPosition(start: SourcePosition) { + global.es2panda._AstNodeSetStart(global.context, this.peer, start.peer); } -} + public get endPosition(): SourcePosition { + return new SourcePosition(global.es2panda._AstNodeEndConst(global.context, this.peer)); + } + + public set endPosition(end: SourcePosition) { + global.es2panda._AstNodeSetEnd(global.context, this.peer, end.peer); + } +} export class UnsupportedNode extends AstNode { constructor(peer: KPtr) { - super(peer) - + super(peer); } } diff --git a/koala-wrapper/src/arkts-api/peers/Context.ts b/koala-wrapper/src/arkts-api/peers/Context.ts index 48f86043a75ec6175591c87369cc534070a9ab8f..865e83408dc0b0f053698f693e500befa3ffb994 100644 --- a/koala-wrapper/src/arkts-api/peers/Context.ts +++ b/koala-wrapper/src/arkts-api/peers/Context.ts @@ -33,13 +33,25 @@ export class Context extends ArktsObject { global.es2panda._CreateContextFromString(global.config, passString(source), passString(global.filePath)) ); } - static destroyAndRecreate(ast: AstNode): Context { - console.log('[TS WRAPPER] DESTROY AND RECREATE'); - const source = filterSource(ast.dumpSrc()); - global.es2panda._DestroyContext(global.context); - global.compilerContext = Context.createFromString(source); - return new Context(global.context); + static createFromStringWithHistory(source: string): Context { + if (!global.configIsInitialized()) { + throwError(`Config not initialized`); + } + return new Context( + global.es2panda._CreateContextFromStringWithHistory(global.config, passString(source), passString(global.filePath)) + ); + } + + static createCacheContextFromFile( + configPtr: KNativePointer, + fileName: string, + globalContextPtr: KNativePointer, + isExternal: KBoolean + ): Context { + return new Context( + global.es2panda._CreateCacheContextFromFile(configPtr, passString(fileName), globalContextPtr, isExternal) + ); } static createContextGenerateAbcForExternalSourceFiles( @@ -52,6 +64,16 @@ export class Context extends ArktsObject { ); } + + static destroyAndRecreate(ast: AstNode): Context { + console.log('[TS WRAPPER] DESTROY AND RECREATE'); + const source = filterSource(ast.dumpSrc()); + global.es2panda._DestroyContext(global.context); + global.compilerContext = Context.createFromString(source); + + return new Context(global.context); + } + get program(): Program { return new Program(global.es2panda._ContextProgram(this.peer)); } diff --git a/koala-wrapper/src/arkts-api/peers/Diagnostic.ts b/koala-wrapper/src/arkts-api/peers/Diagnostic.ts new file mode 100644 index 0000000000000000000000000000000000000000..08882031a902b302bf3cdf121725a78c71373706 --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/Diagnostic.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { passStringArray } from '../utilities/private'; +import { SourcePosition } from './SourcePosition'; +import { SourceRange } from './SourceRange'; +import { DiagnosticInfo } from './DiagnosticInfo'; +import { SuggestionInfo } from './SuggestionInfo'; + +export class Diagnostic extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + static logDiagnostic(kind: DiagnosticKind, pos: SourcePosition, ...args: string[]): void { + global.es2panda._LogDiagnostic(global.context, kind.peer, passStringArray(args), args.length, pos.peer); + } + + static logDiagnosticWithSuggestion(diagnosticInfo: DiagnosticInfo, suggestionInfo: SuggestionInfo): void { + global.es2panda._LogDiagnosticWithSuggestion(global.context, diagnosticInfo.peer, suggestionInfo.peer); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts b/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts new file mode 100644 index 0000000000000000000000000000000000000000..de35eed7cb7675eef3ebcee004892ae4439cb92d --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { SourcePosition } from './SourcePosition'; +import { passStringArray } from '../utilities/private'; + +export class DiagnosticInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, pos: SourcePosition, ...args: string[]): DiagnosticInfo { + return new DiagnosticInfo( + global.es2panda._CreateDiagnosticInfo(global.context, kind.peer, passStringArray(args), args.length, pos.peer) + ); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts b/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts new file mode 100644 index 0000000000000000000000000000000000000000..615765493354be2caf511901fbdc2c8ce1e9aae8 --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/DiagnosticKind.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; + +export enum PluginDiagnosticType { + ES2PANDA_PLUGIN_WARNING = 0, + ES2PANDA_PLUGIN_ERROR, + ES2PANDA_PLUGIN_SUGGESTION, +}; + +export class DiagnosticKind extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(message: string, type: PluginDiagnosticType): DiagnosticKind { + return new DiagnosticKind( + global.es2panda._CreateDiagnosticKind(global.context, message, type) + ); + } +} diff --git a/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts b/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts index eeeecf1dc3efbf2b8bafbc74c554e80429345195..23e4ab7820475d95e77db77be44a723095da54c4 100644 --- a/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts +++ b/koala-wrapper/src/arkts-api/peers/ImportPathManager.ts @@ -29,8 +29,4 @@ export class ImportPathManager extends ArktsObject { global.es2panda._ETSParserGetImportPathManager(global.context) ); } - - resolvePath(currentModulePath: string, importPath: string): string { - return ''; // TODO: no longer support this. - } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/Program.ts b/koala-wrapper/src/arkts-api/peers/Program.ts index 5f3afbed2d791a58cd359de519e2a3d8412cff2e..6cdcc883b53eb087580222c91513b9b23caf9e8c 100644 --- a/koala-wrapper/src/arkts-api/peers/Program.ts +++ b/koala-wrapper/src/arkts-api/peers/Program.ts @@ -19,9 +19,18 @@ import { acceptNativeObjectArrayResult, unpackString } from "../utilities/privat import { KNativePointer } from "@koalaui/interop" import { EtsScript } from "../types" +enum SkipPhaseResult { + NOT_COMPUTED, + CAN_SKIP, + CANNOT_SKIP, +} + export class Program extends ArktsObject { + private canSkipPhaseResult: SkipPhaseResult; + constructor(peer: KNativePointer) { super(peer); + this.canSkipPhaseResult = SkipPhaseResult.NOT_COMPUTED; } get astNode(): EtsScript { @@ -35,17 +44,58 @@ export class Program extends ArktsObject { ); } - get programFileName(): string { + get directExternalSources(): ExternalSource[] { + return acceptNativeObjectArrayResult( + global.es2panda._ProgramDirectExternalSources(global.context, this.peer), + (instance: KNativePointer) => new ExternalSource(instance) + ); + } + + get fileName(): string { return unpackString(global.es2panda._ProgramFileNameConst(global.context, this.peer)); } - get programFileNameWithExtension(): string { + get fileNameWithExtension(): string { return unpackString(global.es2panda._ProgramFileNameWithExtensionConst(global.context, this.peer)); } - get programGlobalAbsName(): string { + /** + * @deprecated + */ + get globalAbsName(): string { return unpackString(global.es2panda._ETSParserGetGlobalProgramAbsName(global.context)); } + + get absName(): string { + return unpackString(global.es2panda._ProgramAbsoluteNameConst(global.context, this.peer)); + } + + get moduleName(): string { + return unpackString(global.es2panda._ProgramModuleNameConst(global.context, this.peer)); + } + + get sourceFilePath(): string { + return unpackString(global.es2panda._ProgramSourceFilePathConst(global.context, this.peer)); + } + + isASTLowered(): boolean { + return global.es2panda._ProgramIsASTLoweredConst(global.context, this.peer); + } + + canSkipPhases(): boolean { + if (this.canSkipPhaseResult === SkipPhaseResult.CAN_SKIP) { + return true; + } else if (this.canSkipPhaseResult === SkipPhaseResult.CANNOT_SKIP) { + return false; + } + if (global.es2panda._ProgramCanSkipPhases(global.context, this.peer)) { + this.canSkipPhaseResult = SkipPhaseResult.CAN_SKIP; + return true; + } else { + this.canSkipPhaseResult = SkipPhaseResult.CANNOT_SKIP; + return false; + } + } } export class ExternalSource extends ArktsObject { diff --git a/koala-wrapper/src/arkts-api/peers/SourcePosition.ts b/koala-wrapper/src/arkts-api/peers/SourcePosition.ts index 9d84847a6c09a6c588e278f10c9b1fded3da0ff3..ff5abb25e40d34b1db15e00642cbef62662e4903 100644 --- a/koala-wrapper/src/arkts-api/peers/SourcePosition.ts +++ b/koala-wrapper/src/arkts-api/peers/SourcePosition.ts @@ -35,4 +35,8 @@ export class SourcePosition extends ArktsObject { line(): number { return global.es2panda._SourcePositionLine(global.context, this.peer); } + + col(): number { + return global.es2panda._SourcePositionCol(global.context, this.peer); + } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts b/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts new file mode 100644 index 0000000000000000000000000000000000000000..2155597bff7852f5ded0c43b11516b02415edd20 --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { SourceRange } from './SourceRange'; +import { passStringArray } from '../utilities/private'; + +export class SuggestionInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, substitutionCode: string, title: string, range: SourceRange, ...args: string[]): SuggestionInfo { + return new SuggestionInfo( + global.es2panda._CreateSuggestionInfo(global.context, kind.peer, passStringArray(args), + args.length, substitutionCode, title, range.peer) + ); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/static/global.ts b/koala-wrapper/src/arkts-api/static/global.ts index 551b6c83f295771f6ce0b57db4204766d55e354e..fe5e598a28b62820255661706fec8087f18078fa 100644 --- a/koala-wrapper/src/arkts-api/static/global.ts +++ b/koala-wrapper/src/arkts-api/static/global.ts @@ -67,7 +67,9 @@ export class global { private static _interop: InteropNativeModule | undefined = undefined public static get interop(): InteropNativeModule { - if (this._interop === undefined) this._interop = initInterop() + if (this._interop === undefined) { + this._interop = initInterop() + } return this._interop } diff --git a/koala-wrapper/src/arkts-api/types.ts b/koala-wrapper/src/arkts-api/types.ts index b587939025e61dc49be6a17b78ca3dd88404c5c2..fe5a2bbc4899cbc298a5324f07383d3d861d7e95 100644 --- a/koala-wrapper/src/arkts-api/types.ts +++ b/koala-wrapper/src/arkts-api/types.ts @@ -27,7 +27,6 @@ import { allFlags, arrayOfNullptr, assertValidPeer, - nodeType, passNode, passNodeArray, passString, @@ -90,7 +89,7 @@ export class EtsScript extends AstNode { global.config = Config.createDefault().peer; } global.compilerContext = Context.createFromString(source); - proceedToState(state); + proceedToState(state, global.context); return new EtsScript( global.es2panda._ProgramAst(global.context, global.es2panda._ContextProgram(global.context)) ); @@ -121,6 +120,10 @@ export class EtsScript extends AstNode { nodes.length ); } + + get isNamespace(): boolean { + return global.generatedEs2panda._ETSModuleIsNamespaceConst(global.context, this.peer); + } } export class ExpressionStatement extends AstNode { @@ -241,6 +244,14 @@ export class CallExpression extends Expression { return this; } + get hasTrailingComma(): boolean { + return global.generatedEs2panda._CallExpressionHasTrailingCommaConst(global.context, this.peer); + } + + get isTrailingCall(): boolean { + return global.es2panda._CallExpressionIsTrailingCallConst(global.context, this.peer); + } + readonly expression: AstNode; // Expression readonly typeArguments: readonly TypeNode[] | undefined; readonly arguments: readonly Expression[]; @@ -473,7 +484,7 @@ export class FunctionDeclaration extends AstNode { readonly isAnon: boolean; } -export class FunctionExpression extends AstNode { +class FunctionExpression extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION); super(peer); diff --git a/koala-wrapper/src/arkts-api/utilities/nodeCache.ts b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts new file mode 100644 index 0000000000000000000000000000000000000000..046b515367307e4585a72fb2355c588cdcd08b04 --- /dev/null +++ b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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 { KNativePointer } from '@koalaui/interop'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +import { AstNode, UnsupportedNode } from '../peers/AstNode'; +import { global } from '../static/global'; +import { getOrPut, nodeByType } from '../class-by-peer'; + +export interface AstNodeCacheValue { + peer: KNativePointer; + type: Es2pandaAstNodeType; + metadata?: AstNodeCacheValueMetadata; +} + +export interface AstNodeCacheValueMetadata { + callName?: string; + hasReceiver?: boolean; + isSetter?: boolean; + isGetter?: boolean; + forbidTypeRewrite?: boolean; + isWithinTypeParams?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; +} + +export class NodeCache { + private _isCollected: boolean = false; + private cacheMap: Map; + private static instance: NodeCache; + + private constructor() { + this.cacheMap = new Map(); + } + + static getInstance(): NodeCache { + if (!this.instance) { + this.instance = new NodeCache(); + } + return this.instance; + } + + collect(node: AstNode, metadata?: AstNodeCacheValueMetadata): void { + const peer = node.peer; + const type = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + let currMetadata: AstNodeCacheValueMetadata | undefined = metadata ?? {}; + if (this.cacheMap.has(peer)) { + const oldMetadata = this.cacheMap.get(peer)!.metadata ?? {}; + currMetadata = { ...oldMetadata, ...currMetadata }; + } + currMetadata = Object.keys(currMetadata).length === 0 ? undefined : currMetadata; + this.cacheMap.set(peer, { peer, type, metadata: currMetadata }); + this._isCollected = true; + } + + refresh(original: AstNode, node: AstNode): void { + let metadata: AstNodeCacheValueMetadata | undefined; + if (this.has(original)) { + metadata = this.get(original)?.metadata; + this.cacheMap.delete(original.peer); + } + this.collect(node, metadata); + } + + isCollected(): boolean { + return this._isCollected; + } + + has(node: AstNode): boolean { + return this.cacheMap.has(node.peer); + } + + get(node: AstNode): AstNodeCacheValue | undefined { + return this.cacheMap.get(node.peer); + } + + clear(): void { + this.cacheMap.clear(); + this._isCollected = false; + } + + visualize(): void { + Array.from(this.cacheMap.values()).forEach(({ peer, type, metadata }) => { + const node = nodeByType.get(type) ?? UnsupportedNode; + const newNode = getOrPut(peer, (peer) => new node(peer)) as AstNode; + console.log( + `[NODE CACHE] ptr ${peer}, type: ${type}, metadata: ${JSON.stringify(metadata)}, node: `, + newNode.dumpSrc() + ); + }); + } +} diff --git a/koala-wrapper/src/arkts-api/utilities/performance.ts b/koala-wrapper/src/arkts-api/utilities/performance.ts index bafe9df5d3e40028a56541ac3e73662df8c58f67..9aaa414e85a7f14db56b68ad73c37b22ee341df3 100644 --- a/koala-wrapper/src/arkts-api/utilities/performance.ts +++ b/koala-wrapper/src/arkts-api/utilities/performance.ts @@ -13,6 +13,22 @@ * limitations under the License. */ +import * as process from 'process'; +import { global as localGlobal} from '../static/global'; + +const BYTES_PER_KIBIBYTE = 1024; + +interface MemoryContext { + startTime: number; + startMemory: { + rss: number; + heapTotal: number; + heapUsed: number; + external: number; + arrayBuffers: number; + }; +} + interface Event { name: string, startTime: number, @@ -34,17 +50,26 @@ function pad(value: number, length: number): string { return value.toString().padStart(length, '0'); } +function round(value: number, index: number = 2): number { + const factor = Math.pow(10, index); + return Math.round(value * factor) / factor; +} + export class Performance { private static instance: Performance; private events: Map; + private historyEvents = new Map(); private scopes: string[]; private shouldSkip: boolean; private totalDuration: number; - + private memoryContexts = new Map(); + private memoryTrackerEnable: boolean; private constructor() { this.events = new Map(); + this.historyEvents = new Map(); this.scopes = []; this.shouldSkip = true; + this.memoryTrackerEnable = false; this.totalDuration = 0; } @@ -59,8 +84,14 @@ export class Performance { this.shouldSkip = shouldSkip; } + enableMemoryTracker(enableMemoryTracker: boolean = false): void { + this.memoryTrackerEnable = enableMemoryTracker; + } + createEvent(name: string): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } const startTime: number = performance.now(); const newEvent: Event = { name, startTime }; this.events.set(name, newEvent); @@ -69,7 +100,9 @@ export class Performance { } stopEvent(name: string, shouldLog: boolean = false): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } if (!this.events.has(name) || this.scopes.length === 0) { throw new Error(`Event ${name} is not created.`); } @@ -82,19 +115,26 @@ export class Performance { const endTime: number = performance.now(); const parentEvent: string = this.scopes[this.scopes.length - 1]; const duration: number = endTime - event.startTime; - this.totalDuration += duration; + if (!parentEvent) { + this.totalDuration += duration; + } if (shouldLog) { console.log( - `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}, total: ${formatTime(this.totalDuration)}` + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` ); } - return { ...event, endTime, parentEvent, duration }; + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; } stopLastEvent(shouldLog: boolean = false): Event { - if (this.shouldSkip) return { name: '', startTime: 0 }; + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } if (this.scopes.length === 0) { throw new Error("No last event"); } @@ -107,19 +147,26 @@ export class Performance { const endTime: number = performance.now(); const parentEvent: string = this.scopes[this.scopes.length - 1]; const duration: number = endTime - event.startTime; - this.totalDuration += duration; + if (!parentEvent) { + this.totalDuration += duration; + } if (shouldLog) { console.log( - `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}, total: ${formatTime(this.totalDuration)}` + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` ); } - return { ...event, endTime, parentEvent, duration }; + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; } clearAllEvents(shouldLog: boolean = false): void { - if (this.shouldSkip) return; + if (this.shouldSkip) { + return; + } for (let i = 0; i < this.scopes.length; i ++) { this.stopLastEvent(shouldLog); } @@ -129,4 +176,134 @@ export class Performance { clearTotalDuration(): void { this.totalDuration = 0; } + + clearHistory(): void { + this.historyEvents = new Map(); + } + + visualizeEvents(shouldLog: boolean = false): void { + if (this.shouldSkip) { + return; + } + const that = this; + function buildVisualization(parentKey: string | null, indentLevel: number): [string, number] { + const children = that.historyEvents.get(parentKey) || []; + let result = ''; + + children.forEach(child => { + const indent = ' '.repeat(indentLevel); + const duration = child.duration ?? 0; + const [_result, count] = buildVisualization(child.name, indentLevel + 1); + result += `${indent}- ${child.name}: ${formatTime(duration)}(${round(duration)}), ${count}\n`; + result += _result; + }); + + return [result, children.length]; + } + + const [finalResult, _] = buildVisualization(null, 0); + if (shouldLog) { + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + console.log(`TOTAL: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})`); + console.log(finalResult.trimEnd()); + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + } + } + + startMemRecord(label: string = `measurement-${Date.now()}`): void { + // 强制进行垃圾回收(需要 Node.js 启动时添加 --expose-gc 参数) + if (!this.memoryTrackerEnable) { + return; + } + if (global.gc) { + (global as any).gc(); + } + const startMemory = process.memoryUsage(); + this.memoryContexts.set(label, { + startTime: Date.now(), + startMemory: { + rss: startMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + heapTotal: startMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + heapUsed: startMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + external: startMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE), + arrayBuffers: (startMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) + } + }); + + return; + } + + stopMemRecord(label: string = `measurement-${Date.now()}`, runGc: boolean = false): void { + if (!this.memoryTrackerEnable) { + return; + } + const context = this.memoryContexts.get(label); + + if (!context) { + console.error(`未找到标签为 "${label}" 的内存测量上下文`); + return; + } + + // 可选:在测量结束前执行垃圾回收 + if (runGc && global.gc) { + (global as any).gc(); + } + + // 记录结束时的内存使用情况 + const endTime = Date.now(); + const endMemory = process.memoryUsage(); + + // 计算内存使用增量 + const memoryDiff = { + rss: endMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.rss, + heapTotal: endMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.heapTotal, + heapUsed: endMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.heapUsed, + external: endMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE) - context.startMemory.external, + arrayBuffers: ((endMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)) - context.startMemory.arrayBuffers + }; + const duration = endTime - context.startTime; + + console.log('[PERFORMANCE]', `内存测量结果 [标签: ${label}]`); + console.log('[PERFORMANCE]', `执行时间: ${duration}ms`); + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE]', `内存类型 | 开始值(MB) | 结束值(MB) | 增量(MB)`); + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE]', `RSS | ${context.startMemory.rss.toFixed(2)} | ${(endMemory.rss / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.rss.toFixed(2)}`); + console.log('[PERFORMANCE]', `Heap Total | ${context.startMemory.heapTotal.toFixed(2)} | ${(endMemory.heapTotal / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.heapTotal.toFixed(2)}`); + console.log('[PERFORMANCE]', `Heap Used | ${context.startMemory.heapUsed.toFixed(2)} | ${(endMemory.heapUsed / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.heapUsed.toFixed(2)}`); + console.log('[PERFORMANCE]', `External | ${context.startMemory.external.toFixed(2)} | ${(endMemory.external / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.external.toFixed(2)}`); + if (endMemory.arrayBuffers !== undefined) { + console.log(`Array Buffers | ${context.startMemory.arrayBuffers.toFixed(2)} | ${((endMemory.arrayBuffers || 0) / (BYTES_PER_KIBIBYTE * BYTES_PER_KIBIBYTE)).toFixed(2)} | ${memoryDiff.arrayBuffers.toFixed(2)}`); + } + console.log('---------------------------------------------------------------'); + this.memoryContexts.delete(label); + return; + } + + memoryTrackerReset(): void { + if (!this.memoryTrackerEnable) { + return; + } + localGlobal.es2panda._MemoryTrackerReset(localGlobal.context); + } + + memoryTrackerGetDelta(tag: string): void { + if (!this.memoryTrackerEnable) { + return; + } + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE] Increamental memory:', tag); + localGlobal.es2panda._MemoryTrackerGetDelta(localGlobal.context); + console.log('---------------------------------------------------------------'); + } + + memoryTrackerPrintCurrent(tag: string): void { + if (!this.memoryTrackerEnable) { + return; + } + console.log('---------------------------------------------------------------'); + console.log('[PERFORMANCE] Current total memory:', tag); + localGlobal.es2panda._MemoryTrackerPrintCurrent(localGlobal.context); + console.log('---------------------------------------------------------------'); + } } \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/utilities/private.ts b/koala-wrapper/src/arkts-api/utilities/private.ts index 30db3e4720c4b3f71d8b06dd0ff5d8727721ed8e..c343fb8e26c9bdd17782f2cc01ab267d10948ee0 100644 --- a/koala-wrapper/src/arkts-api/utilities/private.ts +++ b/koala-wrapper/src/arkts-api/utilities/private.ts @@ -168,10 +168,6 @@ export function updateNodeByNode(node: T, original: AstNode): return node; } -export function nodeType(node: AstNode): Es2pandaAstNodeType { - return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); -} - /** * @deprecated */ diff --git a/koala-wrapper/src/arkts-api/utilities/public.ts b/koala-wrapper/src/arkts-api/utilities/public.ts index 6c9f010d417edb7ad1b27228009c93aa3c202bc6..bf15ef63b6c9bbb560f0fcf2cb680d993a80846f 100644 --- a/koala-wrapper/src/arkts-api/utilities/public.ts +++ b/koala-wrapper/src/arkts-api/utilities/public.ts @@ -15,8 +15,8 @@ import { global } from '../static/global'; import { isNumber, throwError, getEnumName } from '../../utils'; -import { KNativePointer, KInt, nullptr, withStringResult } from '@koalaui/interop'; -import { passNode, passString, unpackNodeArray, unpackNonNullableNode } from './private'; +import { KNativePointer, KInt, nullptr, withStringResult, KStringArrayPtr } from '@koalaui/interop' +import { passNode, passString, passStringArray, unpackNodeArray, unpackNonNullableNode } from './private'; import { isFunctionDeclaration, isMemberExpression, isMethodDefinition, isNumberLiteral } from '../factory/nodeTests'; import { Es2pandaContextState, @@ -42,43 +42,46 @@ import { Program } from '../peers/Program'; import { clearNodeCache } from '../class-by-peer'; import { SourcePosition } from '../peers/SourcePosition'; import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; -export function proceedToState(state: Es2pandaContextState, forceDtsEmit = false): void { +export function proceedToState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); - if (global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { - processErrorState(state, forceDtsEmit); + if (global.es2panda._ContextState(context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { + clearNodeCache(); + processErrorState(state, context, forceDtsEmit); } - if (state <= global.es2panda._ContextState(global.context)) { + if (state <= global.es2panda._ContextState(context)) { console.log('[TS WRAPPER] PROCEED TO STATE: SKIPPING'); return; } clearNodeCache(); - global.es2panda._ProceedToState(global.context, state); - processErrorState(state, forceDtsEmit); + global.es2panda._ProceedToState(context, state); + processErrorState(state, context, forceDtsEmit); } -function processErrorState(state: Es2pandaContextState, forceDtsEmit = false): void { +function processErrorState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { try { - if ( - global.es2panda._ContextState(global.context) === Es2pandaContextState.ES2PANDA_STATE_ERROR && - !forceDtsEmit - ) { - const errorMessage = withStringResult(global.es2panda._ContextErrorMessage(global.context)); + if (global.es2panda._ContextState(context) === Es2pandaContextState.ES2PANDA_STATE_ERROR && !forceDtsEmit) { + const errorMessage = withStringResult(global.es2panda._ContextErrorMessage(context)); if (errorMessage === undefined) { throwError(`Could not get ContextErrorMessage`); } - const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(global.context)); + const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(context)); if (allErrorMessages === undefined) { throwError(`Could not get AllErrorMessages`); } throwError([`Failed to proceed to ${Es2pandaContextState[state]}`, errorMessage, allErrorMessages].join(`\n`)); } } catch (e) { - global.es2panda._DestroyContext(global.context); + global.es2panda._DestroyContext(context); throw e; } } +export function nodeType(node: AstNode): Es2pandaAstNodeType { + return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); +} + export function startChecker(): boolean { return global.es2panda._CheckerStartChecker(global.context); } @@ -102,7 +105,7 @@ export function getDecl(node: AstNode): AstNode | undefined { if (!!decl) { return decl; } - if (isProperty(node.parent)) { + if (!!node.parent && isProperty(node.parent)) { return getDeclFromProperty(node.parent); } return undefined; @@ -112,7 +115,7 @@ function getDeclFromProperty(node: Property): AstNode | undefined { if (!node.key) { return undefined; } - if (!isObjectExpression(node.parent)) { + if (!!node.parent && !isObjectExpression(node.parent)) { return getPeerDecl(passNode(node.key)); } return getDeclFromObjectExpressionProperty(node); @@ -214,6 +217,10 @@ export function importDeclarationInsert(node: ETSImportDeclaration, program: Pro global.es2panda._InsertETSImportDeclarationAndParse(global.context, program.peer, node.peer); } +export function getProgramFromAstNode(node: AstNode): Program { + return new Program(global.es2panda._AstNodeProgram(global.context, node.peer)); +} + export function hasModifierFlag(node: AstNode, flag: Es2pandaModifierFlags): boolean { if (!node) return false; @@ -250,7 +257,7 @@ export function destroyConfig(config: KNativePointer): void { global.resetConfig(); } -export function setAllParents(ast: AstNode) { +export function setAllParents(ast: AstNode): void { global.es2panda._AstNodeUpdateAll(global.context, ast.peer); } @@ -258,6 +265,7 @@ export function generateTsDeclarationsFromContext( outputDeclEts: string, outputEts: string, exportAll: boolean, + isolated: boolean, recordFile: string ): KInt { return global.es2panda._GenerateTsDeclarationsFromContext( @@ -265,10 +273,18 @@ export function generateTsDeclarationsFromContext( passString(outputDeclEts), passString(outputEts), exportAll, + isolated, passString(recordFile) ); } +export function generateStaticDeclarationsFromContext(outputPath: string): KInt { + return global.es2panda._GenerateStaticDeclarationsFromContext( + global.context, + passString(outputPath) + ); +} + export function isDefaultAccessModifierClassProperty(property: ClassProperty): boolean { return global.es2panda._ClassPropertyIsDefaultAccessModifierConst(global.context, property.peer); } @@ -280,3 +296,45 @@ export function getStartPosition(node: AstNode): SourcePosition { export function getEndPosition(node: AstNode): SourcePosition { return new SourcePosition(global.es2panda._AstNodeEndConst(global.context, node.peer)); } + +export function MemInitialize(): void { + global.es2panda._MemInitialize(); +} + +export function MemFinalize(): void { + global.es2panda._MemFinalize(); +} + +export function CreateGlobalContext( + config: KNativePointer, + externalFileList: string[], + fileNum: KInt, + lspUsage: boolean +): KNativePointer { + return global.es2panda._CreateGlobalContext(config, passStringArray(externalFileList), fileNum, lspUsage); +} + +export function DestroyGlobalContext(context: KNativePointer): void { + global.es2panda._DestroyGlobalContext(context); +} + +export function CreateCacheContextFromFile( + configPtr: KNativePointer, + filename: string, + globalContext: KNativePointer, + isExternal: Boolean +): KNativePointer { + return global.es2panda._CreateCacheContextFromFile(configPtr, passString(filename), globalContext, isExternal); +} + +export function createTypeNodeFromTsType(node: AstNode): AstNode | undefined { + const typeAnnotation = global.es2panda._CreateTypeNodeFromTsType(global.context, node.peer); + if (typeAnnotation === nullptr) { + return undefined; + } + return unpackNonNullableNode(typeAnnotation); +} + +export function getJsdocStringFromDeclaration(decl: AstNode): string { + return withStringResult(global.es2panda._JsdocStringFromDeclaration(global.context, decl.peer)) ?? throwError(`failed to unpack (peer shouldn't be NULLPTR)`); +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/visitor.ts b/koala-wrapper/src/arkts-api/visitor.ts index e6ebd25db323340f305e06d6f81367e8576249cb..7898501f2cdba9066a4bcc53da78d25bdea3786d 100644 --- a/koala-wrapper/src/arkts-api/visitor.ts +++ b/koala-wrapper/src/arkts-api/visitor.ts @@ -52,7 +52,14 @@ import { isForUpdateStatement, isForOfStatement, isTSTypeAliasDeclaration, + isETSParameterExpression, isETSFunctionType, + isSwitchStatement, + isSwitchCaseStatement, + isSpreadElement, + isClassStaticBlock, + isFunctionExpression, + FunctionExpression, } from '../generated'; import { isEtsScript, @@ -72,67 +79,10 @@ import { } from './factory/nodeTests'; import { classDefinitionFlags } from './utilities/public'; import { Es2pandaAstNodeType } from '../Es2pandaEnums'; +import { updateFunctionExpression } from './node-utilities/FunctionExpression'; type Visitor = (node: AstNode) => AstNode; -export interface StructVariableMetadata { - name: string; - properties: string[]; - modifiers: Es2pandaModifierFlags; - hasStateManagementType?: boolean; -} - -export class StructInfo { - metadata: Record = {}; - initializeBody: AstNode[] = []; - updateBody: AstNode[] = []; - isReusable: boolean = false; - toRecordBody: Property[] = []; -} - -export class GlobalInfo { - private _structCollection: Set; - private static instance: GlobalInfo; - private _structMap: Map; - - private constructor() { - this._structCollection = new Set(); - this._structMap = new Map(); - } - - public static getInfoInstance(): GlobalInfo { - if (!this.instance) { - this.instance = new GlobalInfo(); - } - return this.instance; - } - - public add(str: string): void { - this._structCollection.add(str); - } - - public getStructCollection(): Set { - return this._structCollection; - } - - public getStructInfo(structName: string): StructInfo { - const structInfo = this._structMap.get(structName); - if (!structInfo) { - return new StructInfo(); - } - return structInfo; - } - - public setStructInfo(structName: string, info: StructInfo): void { - this._structMap.set(structName, info); - } - - public reset(): void { - this._structMap.clear(); - this._structCollection.clear(); - } -} - // TODO: rethink (remove as) function nodeVisitor(node: T, visitor: Visitor): T { if (node === undefined) { @@ -163,6 +113,7 @@ export function visitEachChild(node: AstNode, visitor: Visitor): AstNode { script = visitDefinitionBody(script, visitor); script = visitStatement(script, visitor); script = visitForLoopStatement(script, visitor); + script = visitSwitchCaseStatement(script, visitor); script = visitOuterExpression(script, visitor); script = visitInnerExpression(script, visitor); script = visitTrivialExpression(script, visitor); @@ -204,7 +155,7 @@ function visitOuterExpression(node: AstNode, visitor: Visitor): AstNode { updated = true; return factory.updateETSNewClassInstanceExpression( node, - node.getTypeRef, + nodeVisitor(node.getTypeRef, visitor), nodesVisitor(node.getArguments, visitor) ); } @@ -278,6 +229,10 @@ function visitTrivialExpression(node: AstNode, visitor: Visitor): AstNode { node.operatorType ); } + if (isSpreadElement(node)) { + const nodeType = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + return factory.updateSpreadElement(node, nodeType, nodeVisitor(node.argument, visitor)); + } // TODO return node; } @@ -454,6 +409,23 @@ function visitForLoopStatement(node: AstNode, visitor: Visitor): AstNode { return node; } +function visitSwitchCaseStatement(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isSwitchStatement(node)) { + return factory.updateSwitchStatement( + node, + nodeVisitor(node.discriminant, visitor), + nodesVisitor(node.cases, visitor) + ); + } + if (isSwitchCaseStatement(node)) { + return factory.updateSwitchCaseStatement(node, node.test, nodesVisitor(node.consequent, visitor)); + } + return node; +} + function visitETSModule(node: AstNode, visitor: Visitor): AstNode { if (updated) { return node; @@ -495,6 +467,16 @@ function visitDefinitionBody(node: AstNode, visitor: Visitor): AstNode { node.isComputed ); } + if (isClassStaticBlock(node) && !!node.value) { + updated = true; + return factory.updateClassStaticBlock( + node, + updateFunctionExpression( + node.value as FunctionExpression, + nodeVisitor(node.function, visitor) + ) + ); + } // TODO return node; } diff --git a/koala-wrapper/src/generated/Es2pandaEnums.ts b/koala-wrapper/src/generated/Es2pandaEnums.ts index 1e5ee0d2c482bbc7844c58134580b5f277e611cc..249570dde713bc41e5c4f5b713fcddfb072058a4 100644 --- a/koala-wrapper/src/generated/Es2pandaEnums.ts +++ b/koala-wrapper/src/generated/Es2pandaEnums.ts @@ -24,174 +24,7 @@ export enum Es2pandaContextState { ES2PANDA_STATE_BIN_GENERATED = 6, ES2PANDA_STATE_ERROR = 7 } -// export enum Es2pandaAstNodeType { -// AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION = 0, -// AST_NODE_TYPE_ANNOTATION_DECLARATION = 1, -// AST_NODE_TYPE_ANNOTATION_USAGE = 2, -// AST_NODE_TYPE_ASSERT_STATEMENT = 3, -// AST_NODE_TYPE_AWAIT_EXPRESSION = 4, -// AST_NODE_TYPE_BIGINT_LITERAL = 5, -// AST_NODE_TYPE_BINARY_EXPRESSION = 6, -// AST_NODE_TYPE_BLOCK_STATEMENT = 7, -// AST_NODE_TYPE_BOOLEAN_LITERAL = 8, -// AST_NODE_TYPE_BREAK_STATEMENT = 9, -// AST_NODE_TYPE_CALL_EXPRESSION = 10, -// AST_NODE_TYPE_CATCH_CLAUSE = 11, -// AST_NODE_TYPE_CHAIN_EXPRESSION = 12, -// AST_NODE_TYPE_CHAR_LITERAL = 13, -// AST_NODE_TYPE_CLASS_DEFINITION = 14, -// AST_NODE_TYPE_CLASS_DECLARATION = 15, -// AST_NODE_TYPE_CLASS_EXPRESSION = 16, -// AST_NODE_TYPE_CLASS_PROPERTY = 17, -// AST_NODE_TYPE_CLASS_STATIC_BLOCK = 18, -// AST_NODE_TYPE_CONDITIONAL_EXPRESSION = 19, -// AST_NODE_TYPE_CONTINUE_STATEMENT = 20, -// AST_NODE_TYPE_DEBUGGER_STATEMENT = 21, -// AST_NODE_TYPE_DECORATOR = 22, -// AST_NODE_TYPE_DIRECT_EVAL = 23, -// AST_NODE_TYPE_DO_WHILE_STATEMENT = 24, -// AST_NODE_TYPE_EMPTY_STATEMENT = 25, -// AST_NODE_TYPE_EXPORT_ALL_DECLARATION = 26, -// AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION = 27, -// AST_NODE_TYPE_EXPORT_NAMED_DECLARATION = 28, -// AST_NODE_TYPE_EXPORT_SPECIFIER = 29, -// AST_NODE_TYPE_EXPRESSION_STATEMENT = 30, -// AST_NODE_TYPE_FOR_IN_STATEMENT = 31, -// AST_NODE_TYPE_FOR_OF_STATEMENT = 32, -// AST_NODE_TYPE_FOR_UPDATE_STATEMENT = 33, -// AST_NODE_TYPE_FUNCTION_DECLARATION = 34, -// AST_NODE_TYPE_FUNCTION_EXPRESSION = 35, -// AST_NODE_TYPE_IDENTIFIER = 36, -// AST_NODE_TYPE_DUMMYNODE = 37, -// AST_NODE_TYPE_IF_STATEMENT = 38, -// AST_NODE_TYPE_IMPORT_DECLARATION = 39, -// AST_NODE_TYPE_IMPORT_EXPRESSION = 40, -// AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER = 41, -// AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER = 42, -// AST_NODE_TYPE_IMPORT_SPECIFIER = 43, -// AST_NODE_TYPE_LABELLED_STATEMENT = 44, -// AST_NODE_TYPE_MEMBER_EXPRESSION = 45, -// AST_NODE_TYPE_META_PROPERTY_EXPRESSION = 46, -// AST_NODE_TYPE_METHOD_DEFINITION = 47, -// AST_NODE_TYPE_NAMED_TYPE = 48, -// AST_NODE_TYPE_NAMESPACE_DECLARATION = 49, -// AST_NODE_TYPE_NAMESPACE_DEFINITION = 50, -// AST_NODE_TYPE_NEW_EXPRESSION = 51, -// AST_NODE_TYPE_NULL_LITERAL = 52, -// AST_NODE_TYPE_UNDEFINED_LITERAL = 53, -// AST_NODE_TYPE_NUMBER_LITERAL = 54, -// AST_NODE_TYPE_OMITTED_EXPRESSION = 55, -// AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION = 56, -// AST_NODE_TYPE_PROPERTY = 57, -// AST_NODE_TYPE_REGEXP_LITERAL = 58, -// AST_NODE_TYPE_REEXPORT_STATEMENT = 59, -// AST_NODE_TYPE_RETURN_STATEMENT = 60, -// AST_NODE_TYPE_SCRIPT_FUNCTION = 61, -// AST_NODE_TYPE_SEQUENCE_EXPRESSION = 62, -// AST_NODE_TYPE_STRING_LITERAL = 63, -// AST_NODE_TYPE_ETS_NULL_TYPE = 64, -// AST_NODE_TYPE_ETS_UNDEFINED_TYPE = 65, -// AST_NODE_TYPE_ETS_NEVER_TYPE = 66, -// AST_NODE_TYPE_ETS_STRING_LITERAL_TYPE = 67, -// AST_NODE_TYPE_ETS_FUNCTION_TYPE = 68, -// AST_NODE_TYPE_ETS_WILDCARD_TYPE = 69, -// AST_NODE_TYPE_ETS_PRIMITIVE_TYPE = 70, -// AST_NODE_TYPE_ETS_PACKAGE_DECLARATION = 71, -// AST_NODE_TYPE_ETS_CLASS_LITERAL = 72, -// AST_NODE_TYPE_ETS_TYPE_REFERENCE = 73, -// AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART = 74, -// AST_NODE_TYPE_ETS_UNION_TYPE = 75, -// AST_NODE_TYPE_ETS_LAUNCH_EXPRESSION = 76, -// AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION = 77, -// AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION = 78, -// AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION = 79, -// AST_NODE_TYPE_ETS_IMPORT_DECLARATION = 80, -// AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION = 81, -// AST_NODE_TYPE_ETS_TUPLE = 82, -// AST_NODE_TYPE_ETS_SCRIPT = 83, -// AST_NODE_TYPE_SUPER_EXPRESSION = 84, -// AST_NODE_TYPE_STRUCT_DECLARATION = 85, -// AST_NODE_TYPE_SWITCH_CASE_STATEMENT = 86, -// AST_NODE_TYPE_SWITCH_STATEMENT = 87, -// AST_NODE_TYPE_TS_ENUM_DECLARATION = 88, -// AST_NODE_TYPE_TS_ENUM_MEMBER = 89, -// AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE = 90, -// AST_NODE_TYPE_TS_NUMBER_KEYWORD = 91, -// AST_NODE_TYPE_TS_ANY_KEYWORD = 92, -// AST_NODE_TYPE_TS_STRING_KEYWORD = 93, -// AST_NODE_TYPE_TS_BOOLEAN_KEYWORD = 94, -// AST_NODE_TYPE_TS_VOID_KEYWORD = 95, -// AST_NODE_TYPE_TS_UNDEFINED_KEYWORD = 96, -// AST_NODE_TYPE_TS_UNKNOWN_KEYWORD = 97, -// AST_NODE_TYPE_TS_OBJECT_KEYWORD = 98, -// AST_NODE_TYPE_TS_BIGINT_KEYWORD = 99, -// AST_NODE_TYPE_TS_NEVER_KEYWORD = 100, -// AST_NODE_TYPE_TS_NON_NULL_EXPRESSION = 101, -// AST_NODE_TYPE_TS_NULL_KEYWORD = 102, -// AST_NODE_TYPE_TS_ARRAY_TYPE = 103, -// AST_NODE_TYPE_TS_UNION_TYPE = 104, -// AST_NODE_TYPE_TS_TYPE_LITERAL = 105, -// AST_NODE_TYPE_TS_PROPERTY_SIGNATURE = 106, -// AST_NODE_TYPE_TS_METHOD_SIGNATURE = 107, -// AST_NODE_TYPE_TS_SIGNATURE_DECLARATION = 108, -// AST_NODE_TYPE_TS_PARENT_TYPE = 109, -// AST_NODE_TYPE_TS_LITERAL_TYPE = 110, -// AST_NODE_TYPE_TS_INFER_TYPE = 111, -// AST_NODE_TYPE_TS_CONDITIONAL_TYPE = 112, -// AST_NODE_TYPE_TS_IMPORT_TYPE = 113, -// AST_NODE_TYPE_TS_INTERSECTION_TYPE = 114, -// AST_NODE_TYPE_TS_MAPPED_TYPE = 115, -// AST_NODE_TYPE_TS_MODULE_BLOCK = 116, -// AST_NODE_TYPE_TS_THIS_TYPE = 117, -// AST_NODE_TYPE_TS_TYPE_OPERATOR = 118, -// AST_NODE_TYPE_TS_TYPE_PARAMETER = 119, -// AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION = 120, -// AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION = 121, -// AST_NODE_TYPE_TS_TYPE_PREDICATE = 122, -// AST_NODE_TYPE_TS_PARAMETER_PROPERTY = 123, -// AST_NODE_TYPE_TS_MODULE_DECLARATION = 124, -// AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION = 125, -// AST_NODE_TYPE_TS_FUNCTION_TYPE = 126, -// AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE = 127, -// AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION = 128, -// AST_NODE_TYPE_TS_TYPE_REFERENCE = 129, -// AST_NODE_TYPE_TS_QUALIFIED_NAME = 130, -// AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE = 131, -// AST_NODE_TYPE_TS_INTERFACE_DECLARATION = 132, -// AST_NODE_TYPE_TS_INTERFACE_BODY = 133, -// AST_NODE_TYPE_TS_INTERFACE_HERITAGE = 134, -// AST_NODE_TYPE_TS_TUPLE_TYPE = 135, -// AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER = 136, -// AST_NODE_TYPE_TS_INDEX_SIGNATURE = 137, -// AST_NODE_TYPE_TS_TYPE_QUERY = 138, -// AST_NODE_TYPE_TS_AS_EXPRESSION = 139, -// AST_NODE_TYPE_TS_CLASS_IMPLEMENTS = 140, -// AST_NODE_TYPE_TS_TYPE_ASSERTION = 141, -// AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION = 142, -// AST_NODE_TYPE_TEMPLATE_ELEMENT = 143, -// AST_NODE_TYPE_TEMPLATE_LITERAL = 144, -// AST_NODE_TYPE_THIS_EXPRESSION = 145, -// AST_NODE_TYPE_TYPEOF_EXPRESSION = 146, -// AST_NODE_TYPE_THROW_STATEMENT = 147, -// AST_NODE_TYPE_TRY_STATEMENT = 148, -// AST_NODE_TYPE_UNARY_EXPRESSION = 149, -// AST_NODE_TYPE_UPDATE_EXPRESSION = 150, -// AST_NODE_TYPE_VARIABLE_DECLARATION = 151, -// AST_NODE_TYPE_VARIABLE_DECLARATOR = 152, -// AST_NODE_TYPE_WHILE_STATEMENT = 153, -// AST_NODE_TYPE_YIELD_EXPRESSION = 154, -// AST_NODE_TYPE_OPAQUE_TYPE_NODE = 155, -// AST_NODE_TYPE_BLOCK_EXPRESSION = 156, -// AST_NODE_TYPE_ERROR_TYPE_NODE = 157, -// AST_NODE_TYPE_ARRAY_EXPRESSION = 158, -// AST_NODE_TYPE_ARRAY_PATTERN = 159, -// AST_NODE_TYPE_ASSIGNMENT_EXPRESSION = 160, -// AST_NODE_TYPE_ASSIGNMENT_PATTERN = 161, -// AST_NODE_TYPE_OBJECT_EXPRESSION = 162, -// AST_NODE_TYPE_OBJECT_PATTERN = 163, -// AST_NODE_TYPE_SPREAD_ELEMENT = 164, -// AST_NODE_TYPE_REST_ELEMENT = 165 -// } + export enum Es2pandaScopeType { SCOPE_TYPE_PARAM = 0, SCOPE_TYPE_CATCH_PARAM = 1, @@ -1261,4 +1094,4 @@ export enum Es2pandaCheckDecision { export enum Es2pandaCheckAction { CHECK_ACTION_CONTINUE = 0, CHECK_ACTION_SKIP_SUBTREE = 1 -} +} \ No newline at end of file diff --git a/koala-wrapper/src/generated/Es2pandaNativeModule.ts b/koala-wrapper/src/generated/Es2pandaNativeModule.ts index 4502c145f54924eaac2e0a0b413888c33996e3b1..78bf7b0e89b2f6b8e02f043435db84050b801230 100644 --- a/koala-wrapper/src/generated/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/generated/Es2pandaNativeModule.ts @@ -115,12 +115,6 @@ export class Es2pandaNativeModule { _ETSFunctionTypeIrFlags(context: KNativePointer, receiver: KNativePointer): KInt { throw new Error("'ETSFunctionTypeIrFlags was not overloaded by native module initialization") } - _ETSFunctionTypeIrIsThrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ETSFunctionTypeIrIsThrowingConst was not overloaded by native module initialization") - } - _ETSFunctionTypeIrIsRethrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ETSFunctionTypeIrIsRethrowingConst was not overloaded by native module initialization") - } _ETSFunctionTypeIrIsExtensionFunctionConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ETSFunctionTypeIrIsExtensionFunctionConst was not overloaded by native module initialization") } @@ -877,9 +871,6 @@ export class Es2pandaNativeModule { _TSTypeAliasDeclarationSetTypeParameters(context: KNativePointer, receiver: KNativePointer, typeParams: KNativePointer): void { throw new Error("'TSTypeAliasDeclarationSetTypeParameters was not overloaded by native module initialization") } - _TSTypeAliasDeclarationAnnotations(context: KNativePointer, receiver: KNativePointer): KNativePointer { - throw new Error("'TSTypeAliasDeclarationAnnotations was not overloaded by native module initialization") - } _TSTypeAliasDeclarationAnnotationsConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'TSTypeAliasDeclarationAnnotationsConst was not overloaded by native module initialization") } @@ -1051,12 +1042,6 @@ export class Es2pandaNativeModule { _ScriptFunctionHasThrowStatementConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ScriptFunctionHasThrowStatementConst was not overloaded by native module initialization") } - _ScriptFunctionIsThrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ScriptFunctionIsThrowingConst was not overloaded by native module initialization") - } - _ScriptFunctionIsRethrowingConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ScriptFunctionIsRethrowingConst was not overloaded by native module initialization") - } _ScriptFunctionIsDynamicConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ScriptFunctionIsDynamicConst was not overloaded by native module initialization") } @@ -1075,9 +1060,6 @@ export class Es2pandaNativeModule { _ScriptFunctionAddFlag(context: KNativePointer, receiver: KNativePointer, flags: KInt): void { throw new Error("'ScriptFunctionAddFlag was not overloaded by native module initialization") } - _ScriptFunctionAddModifier(context: KNativePointer, receiver: KNativePointer, flags: KInt): void { - throw new Error("'ScriptFunctionAddModifier was not overloaded by native module initialization") - } _ScriptFunctionFormalParamsLengthConst(context: KNativePointer, receiver: KNativePointer): KUInt { throw new Error("'ScriptFunctionFormalParamsLengthConst was not overloaded by native module initialization") } @@ -1498,6 +1480,18 @@ export class Es2pandaNativeModule { _ETSTupleSetTypeAnnotationsList(context: KNativePointer, receiver: KNativePointer, typeNodeList: BigUint64Array, typeNodeListSequenceLength: KUInt): void { throw new Error("'ETSTupleSetTypeAnnotationsList was not overloaded by native module initialization") } + _CreateTryStatement(context: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { + throw new Error("'CreateTryStatement was not overloaded by native module initialization") + } + _UpdateTryStatement(context: KNativePointer, original: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { + throw new Error("'UpdateTryStatement was not overloaded by native module initialization") + } + _CreateTryStatement1(context: KNativePointer, other: KNativePointer): KNativePointer { + throw new Error("'CreateTryStatement1 was not overloaded by native module initialization") + } + _UpdateTryStatement1(context: KNativePointer, original: KNativePointer, other: KNativePointer): KNativePointer { + throw new Error("'UpdateTryStatement1 was not overloaded by native module initialization") + } _TryStatementFinallyBlockConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'TryStatementFinallyBlockConst was not overloaded by native module initialization") } @@ -2116,9 +2110,6 @@ export class Es2pandaNativeModule { _ImportDeclarationSpecifiersConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'ImportDeclarationSpecifiersConst was not overloaded by native module initialization") } - _ImportDeclarationSpecifiers(context: KNativePointer, receiver: KNativePointer): KNativePointer { - throw new Error("'ImportDeclarationSpecifiers was not overloaded by native module initialization") - } _ImportDeclarationIsTypeKindConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ImportDeclarationIsTypeKindConst was not overloaded by native module initialization") } @@ -2152,9 +2143,6 @@ export class Es2pandaNativeModule { _ETSImportDeclarationIsPureDynamicConst(context: KNativePointer, receiver: KNativePointer): KBoolean { throw new Error("'ETSImportDeclarationIsPureDynamicConst was not overloaded by native module initialization") } - _ETSImportDeclarationAssemblerName(context: KNativePointer, receiver: KNativePointer): KStringPtr { - throw new Error("'ETSImportDeclarationAssemblerName was not overloaded by native module initialization") - } _ETSImportDeclarationAssemblerNameConst(context: KNativePointer, receiver: KNativePointer): KStringPtr { throw new Error("'ETSImportDeclarationAssemblerNameConst was not overloaded by native module initialization") } @@ -3707,16 +3695,4 @@ export class Es2pandaNativeModule { _CreateFunctionDecl(context: KNativePointer, name: KStringPtr, node: KNativePointer): KNativePointer { throw new Error("'CreateFunctionDecl was not overloaded by native module initialization") } - _CreateTryStatement(context: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { - throw new Error("'CreateTryStatement was not overloaded by native module initialization") - } - _UpdateTryStatement(context: KNativePointer, original: KNativePointer, block: KNativePointer, catchClauses: BigUint64Array, catchClausesSequenceLength: KUInt, finalizer: KNativePointer, finalizerInsertionsLabelPair: BigUint64Array, finalizerInsertionsLabelPairSequenceLength: KUInt, finalizerInsertionsStatement: BigUint64Array, finalizerInsertionsStatementSequenceLength: KUInt): KNativePointer { - throw new Error("'UpdateTryStatement was not overloaded by native module initialization") - } - _CreateTryStatement1(context: KNativePointer, other: KNativePointer): KNativePointer { - throw new Error("'CreateTryStatement1 was not overloaded by native module initialization") - } - _UpdateTryStatement1(context: KNativePointer, original: KNativePointer, other: KNativePointer): KNativePointer { - throw new Error("'UpdateTryStatement1 was not overloaded by native module initialization") - } } diff --git a/koala-wrapper/src/generated/index.ts b/koala-wrapper/src/generated/index.ts index 0416a06912d49fe13ddd404bbe71562bca49a235..81fc18f948a0803b92940947a2398ece77332177 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"; diff --git a/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts b/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts index f1199de4dbd806158f2e28a5a37e42498d2160a1..13274b55fa6c70832985d56a06a345c62f91f238 100644 --- a/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts +++ b/koala-wrapper/src/generated/peers/AnnotatedAstNode.ts @@ -14,19 +14,8 @@ */ import { - global, - passNode, - passNodeArray, - unpackNonNullableNode, - unpackNode, - unpackNodeArray, - assertValidPeer, AstNode, - Es2pandaAstNodeType, - KNativePointer, - nodeByType, - ArktsObject, - unpackString + KNativePointer } from "../../reexport-for-generated" export class AnnotatedAstNode extends AstNode { diff --git a/koala-wrapper/src/generated/peers/AnnotatedExpression.ts b/koala-wrapper/src/generated/peers/AnnotatedExpression.ts index 2a5924eb9e87c8e69b6ab45a59b686f48f808a28..66718209b1cb434d9a8aed52541fd93c1a6d1ece 100644 --- a/koala-wrapper/src/generated/peers/AnnotatedExpression.ts +++ b/koala-wrapper/src/generated/peers/AnnotatedExpression.ts @@ -16,17 +16,9 @@ import { global, passNode, - passNodeArray, - unpackNonNullableNode, unpackNode, - unpackNodeArray, - assertValidPeer, AstNode, - Es2pandaAstNodeType, - KNativePointer, - nodeByType, - ArktsObject, - unpackString + KNativePointer } from "../../reexport-for-generated" import { Expression } from "./Expression" diff --git a/koala-wrapper/src/generated/peers/BreakStatement.ts b/koala-wrapper/src/generated/peers/BreakStatement.ts index 77b609b0aea2d5e4d35b8bf9079ad885953432f4..b897f67a9e27d7d9e39b4f2c5108e5c3c545ad5f 100644 --- a/koala-wrapper/src/generated/peers/BreakStatement.ts +++ b/koala-wrapper/src/generated/peers/BreakStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class BreakStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT) super(pointer) } @@ -64,6 +64,6 @@ export class BreakStatement extends Statement { export function isBreakStatement(node: AstNode): node is BreakStatement { return node instanceof BreakStatement } -if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT)) { - nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT, BreakStatement) -} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT, BreakStatement) +} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/ClassDefinition.ts b/koala-wrapper/src/generated/peers/ClassDefinition.ts index fe51f912be9594aa2f104ce7e9172eaedd3c7141..2da37bb63be51a2acdcae6602d829f1bd109f27e 100644 --- a/koala-wrapper/src/generated/peers/ClassDefinition.ts +++ b/koala-wrapper/src/generated/peers/ClassDefinition.ts @@ -42,6 +42,7 @@ import { TSEnumDeclaration } from "./TSEnumDeclaration" import { ClassDeclaration } from "./ClassDeclaration" import { FunctionExpression } from "./FunctionExpression" import { AnnotationUsage } from "./AnnotationUsage" +import { Es2pandaLanguage } from "../../Es2pandaEnums" export class ClassDefinition extends TypedAstNode { constructor(pointer: KNativePointer) { assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION) @@ -66,6 +67,12 @@ export class ClassDefinition extends TypedAstNode { static update2ClassDefinition(original: ClassDefinition | undefined, ident: Identifier | undefined, modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags): ClassDefinition { return new ClassDefinition(global.generatedEs2panda._UpdateClassDefinition2(global.context, passNode(original), passNode(ident), modifiers, flags)) } + static create3ClassDefinition(ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, _implements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags, lang: Es2pandaLanguage): ClassDefinition { + return new ClassDefinition(global.es2panda._CreateClassDefinition3(global.context, passNode(ident), passNode(typeParams), passNode(superTypeParams), passNodeArray(_implements), _implements.length, passNode(ctor), passNode(superClass), passNodeArray(body), body.length, modifiers, flags, lang)) + } + static update3ClassDefinition(original: ClassDefinition | undefined, ident: Identifier | undefined, typeParams: TSTypeParameterDeclaration | undefined, superTypeParams: TSTypeParameterInstantiation | undefined, _implements: readonly TSClassImplements[], ctor: MethodDefinition | undefined, superClass: Expression | undefined, body: readonly AstNode[], modifiers: Es2pandaClassDefinitionModifiers, flags: Es2pandaModifierFlags, lang: Es2pandaLanguage): ClassDefinition { + return new ClassDefinition(global.es2panda._UpdateClassDefinition3(global.context, passNode(original), passNode(ident), passNode(typeParams), passNode(superTypeParams), passNodeArray(_implements), _implements.length, passNode(ctor), passNode(superClass), passNodeArray(body), body.length, modifiers, flags, lang)) + } get ident(): Identifier | undefined { return unpackNode(global.generatedEs2panda._ClassDefinitionIdentConst(global.context, this.peer)) } @@ -213,6 +220,9 @@ export class ClassDefinition extends TypedAstNode { global.generatedEs2panda._ClassDefinitionSetAnnotations(global.context, this.peer, passNodeArray(annotations), annotations.length) return this } + get lang(): Es2pandaLanguage { + return global.es2panda._ClassDefinitionLanguageConst(global.context, this.peer); + } } export function isClassDefinition(node: AstNode): node is ClassDefinition { return node instanceof ClassDefinition diff --git a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts index 5b2bef593b039d4915de3c85060394e62c60b6c2..eeb2a2a2d34331dc963664b7704c70d7d2315689 100644 --- a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts +++ b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts @@ -34,20 +34,27 @@ import { Expression } from "./Expression" import { ScriptFunction } from "./ScriptFunction" export class ClassStaticBlock extends ClassElement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK) - super(pointer) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK); + super(pointer); } static createClassStaticBlock(value?: Expression): ClassStaticBlock { - return new ClassStaticBlock(global.generatedEs2panda._CreateClassStaticBlock(global.context, passNode(value))) + return new ClassStaticBlock(global.generatedEs2panda._CreateClassStaticBlock(global.context, passNode(value))); } static updateClassStaticBlock(original?: ClassStaticBlock, value?: Expression): ClassStaticBlock { - return new ClassStaticBlock(global.generatedEs2panda._UpdateClassStaticBlock(global.context, passNode(original), passNode(value))) + return new ClassStaticBlock(global.generatedEs2panda._UpdateClassStaticBlock(global.context, passNode(original), passNode(value))); } + get function(): ScriptFunction | undefined { + return unpackNode(global.generatedEs2panda._ClassStaticBlockFunction(global.context, this.peer)); + } + get name(): string { + return unpackString(global.generatedEs2panda._ClassStaticBlockNameConst(global.context, this.peer)); + } + protected readonly brandClassStaticBlock: undefined; } export function isClassStaticBlock(node: AstNode): node is ClassStaticBlock { - return node instanceof ClassStaticBlock + return node instanceof ClassStaticBlock; } if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK)) { - nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, ClassStaticBlock) + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, ClassStaticBlock); } diff --git a/koala-wrapper/src/generated/peers/ETSFunctionType.ts b/koala-wrapper/src/generated/peers/ETSFunctionType.ts index 5a630295c5bd19087cd7435d18ea4e2c70d7fb63..ed8d5f7edbc15944587b1f85ce9d074519cc606f 100644 --- a/koala-wrapper/src/generated/peers/ETSFunctionType.ts +++ b/koala-wrapper/src/generated/peers/ETSFunctionType.ts @@ -67,12 +67,6 @@ export class ETSFunctionType extends TypeNode { get flags(): Es2pandaScriptFunctionFlags { return global.generatedEs2panda._ETSFunctionTypeIrFlags(global.context, this.peer) } - get isThrowing(): boolean { - return global.generatedEs2panda._ETSFunctionTypeIrIsThrowingConst(global.context, this.peer) - } - get isRethrowing(): boolean { - return global.generatedEs2panda._ETSFunctionTypeIrIsRethrowingConst(global.context, this.peer) - } get isExtensionFunction(): boolean { return global.generatedEs2panda._ETSFunctionTypeIrIsExtensionFunctionConst(global.context, this.peer) } diff --git a/koala-wrapper/src/generated/peers/ScriptFunction.ts b/koala-wrapper/src/generated/peers/ScriptFunction.ts index 42c4c9343e2b2c15fcc6f8ff687f96fe887b57da..d04c69b5e0d0d3cb32eea3f5fa996c840e9944da 100644 --- a/koala-wrapper/src/generated/peers/ScriptFunction.ts +++ b/koala-wrapper/src/generated/peers/ScriptFunction.ts @@ -149,12 +149,6 @@ export class ScriptFunction extends AstNode { get hasThrowStatement(): boolean { return global.generatedEs2panda._ScriptFunctionHasThrowStatementConst(global.context, this.peer) } - get isThrowing(): boolean { - return global.generatedEs2panda._ScriptFunctionIsThrowingConst(global.context, this.peer) - } - get isRethrowing(): boolean { - return global.generatedEs2panda._ScriptFunctionIsRethrowingConst(global.context, this.peer) - } get isDynamic(): boolean { return global.generatedEs2panda._ScriptFunctionIsDynamicConst(global.context, this.peer) } @@ -179,7 +173,7 @@ export class ScriptFunction extends AstNode { } /** @deprecated */ addModifier(flags: Es2pandaModifierFlags): this { - global.generatedEs2panda._ScriptFunctionAddModifier(global.context, this.peer, flags) + global.generatedEs2panda._AstNodeAddModifier(global.context, this.peer, flags) return this } get annotations(): readonly AnnotationUsage[] { diff --git a/koala-wrapper/src/generated/peers/SpreadElement.ts b/koala-wrapper/src/generated/peers/SpreadElement.ts index 4b721b9937fa524b766c9f1fee3ed6967cfde0f8..99aa74efe9913a92585551165d6fafd603fd6265 100644 --- a/koala-wrapper/src/generated/peers/SpreadElement.ts +++ b/koala-wrapper/src/generated/peers/SpreadElement.ts @@ -22,46 +22,77 @@ import { unpackNodeArray, assertValidPeer, AstNode, - Es2pandaAstNodeType, KNativePointer, nodeByType, ArktsObject, - unpackString -} from "../../reexport-for-generated" + unpackString, + Es2pandaAstNodeType, +} from '../../reexport-for-generated'; + +import { AnnotatedExpression } from './AnnotatedExpression'; +import { Decorator } from './Decorator'; +import { Expression } from './Expression'; +import { TypeNode } from './TypeNode'; +import { ValidationInfo } from './ValidationInfo'; -import { AnnotatedExpression } from "./AnnotatedExpression" -import { Expression } from "./Expression" -import { Decorator } from "./Decorator" -import { ValidationInfo } from "./ValidationInfo" -import { TypeNode } from "./TypeNode" export class SpreadElement extends AnnotatedExpression { - constructor(pointer: KNativePointer) { - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT); + super(pointer); + } + static createSpreadElement(nodeType: Es2pandaAstNodeType, argument?: Expression): SpreadElement { + const result: SpreadElement = new SpreadElement( + global.generatedEs2panda._CreateSpreadElement(global.context, nodeType, passNode(argument)) + ); + result.updateChildren(); + return result; + } + static updateSpreadElement( + original: SpreadElement | undefined, + nodeType: Es2pandaAstNodeType, + argument?: Expression + ): SpreadElement { + const result: SpreadElement = new SpreadElement( + global.generatedEs2panda._UpdateSpreadElement( + global.context, + passNode(original), + nodeType, + passNode(argument) + ) + ); + result.updateChildren(); + return result; } get argument(): Expression | undefined { - return unpackNode(global.generatedEs2panda._SpreadElementArgumentConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._SpreadElementArgument(global.context, this.peer)); } get isOptional(): boolean { - return global.generatedEs2panda._SpreadElementIsOptionalConst(global.context, this.peer) + return global.generatedEs2panda._SpreadElementIsOptionalConst(global.context, this.peer); } get decorators(): readonly Decorator[] { - return unpackNodeArray(global.generatedEs2panda._SpreadElementDecoratorsConst(global.context, this.peer)) + return unpackNodeArray(global.generatedEs2panda._SpreadElementDecoratorsConst(global.context, this.peer)); } /** @deprecated */ setOptional(optional_arg: boolean): this { - global.generatedEs2panda._SpreadElementSetOptional(global.context, this.peer, optional_arg) - return this + global.generatedEs2panda._SpreadElementSetOptional(global.context, this.peer, optional_arg); + return this; + } + get validateExpression(): ValidationInfo | undefined { + return new ValidationInfo(global.generatedEs2panda._SpreadElementValidateExpression(global.context, this.peer)); } get typeAnnotation(): TypeNode | undefined { - return unpackNode(global.generatedEs2panda._SpreadElementTypeAnnotationConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._SpreadElementTypeAnnotationConst(global.context, this.peer)); } /** @deprecated */ - setTsTypeAnnotation(typeAnnotation: TypeNode): this { - global.generatedEs2panda._SpreadElementSetTsTypeAnnotation(global.context, this.peer, passNode(typeAnnotation)) - return this + setTsTypeAnnotation(typeAnnotation?: TypeNode): this { + global.generatedEs2panda._SpreadElementSetTsTypeAnnotation(global.context, this.peer, passNode(typeAnnotation)); + return this; } + protected readonly brandSpreadElement: undefined; +} +export function isSpreadElement(node: object | undefined): node is SpreadElement { + return node instanceof SpreadElement; +} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SPREAD_ELEMENT, SpreadElement); } -export function isSpreadElement(node: AstNode): node is SpreadElement { - return node instanceof SpreadElement -} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TryStatement.ts b/koala-wrapper/src/generated/peers/TryStatement.ts index 72141cddca83e7d7380d4e79f40056574d98edd6..d6519a34770e054394abe80ccbf23edea8f956f2 100644 --- a/koala-wrapper/src/generated/peers/TryStatement.ts +++ b/koala-wrapper/src/generated/peers/TryStatement.ts @@ -26,7 +26,7 @@ import { nodeByType, ArktsObject, unpackString, - Es2pandaAstNodeType + Es2pandaAstNodeType, } from '../../reexport-for-generated'; import { BlockStatement } from './BlockStatement'; import { CatchClause } from './CatchClause';