diff --git a/arkui-plugins/.gitignore b/arkui-plugins/.gitignore index 7f8e5351e9880229e0bb82f8c68df7b0d9dbf19c..98f9b3e907e12eb5eaac00e962654cb76f9091a0 100644 --- a/arkui-plugins/.gitignore +++ b/arkui-plugins/.gitignore @@ -3,6 +3,7 @@ node_modules/ **/*/dist/ **/*/build/ +dist/ build/ lib/ @@ -13,5 +14,6 @@ package-lock.json coverage/ **/*/generated +**/*/report test/demo/localtest/build_config.json diff --git a/arkui-plugins/BUILD.gn b/arkui-plugins/BUILD.gn index a262ea4c0264b8fde5bce5609afb7a5a503c1c19..707355e1d2cf1ab6080c64b7799320b8f742e4c9 100755 --- a/arkui-plugins/BUILD.gn +++ b/arkui-plugins/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/ohos.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") npm_path = "//prebuilts/build-tools/common/nodejs/current/bin/npm" @@ -32,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" @@ -41,3 +59,11 @@ ohos_copy("ui_plugin") { subsystem_name = "developtools" part_name = "ace_ets2bundle" } + +ohos_copy("ohos_ets_ui_plugins") { + deps = [ ":gen_ui_plugins", ":build_ets_sysResource" ] + sources = [ rebase_path("$target_gen_dir") ] + outputs = [ ohos_ets_ui_plugins_path ] + subsystem_name = "developtools" + part_name = "ace_ets2bundle" +} \ No newline at end of file diff --git a/arkui-plugins/build_ui_plugins.py b/arkui-plugins/build_ui_plugins.py index 3fe73510cf7ab2625b14239abb79ef3c60e7beb0..6ae5d669e05bb65f03fa3825e81dd7da2ac49eff 100755 --- a/arkui-plugins/build_ui_plugins.py +++ b/arkui-plugins/build_ui_plugins.py @@ -53,6 +53,9 @@ def copy_output(options): copy_files(os.path.join(options.source_path, 'lib'), os.path.join(options.output_path, 'lib')) + copy_files(os.path.join(options.source_path, '../compiler/components'), + os.path.join(options.output_path, 'lib/components')) + copy_files(os.path.join(options.source_path, 'package.json'), os.path.join(options.output_path, 'package.json'), True) diff --git a/arkui-plugins/collectors/memo-collectors/factory.ts b/arkui-plugins/collectors/memo-collectors/factory.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1d6e17454c127feec7599709a30a131149aec9a --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/factory.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 arkts from '@koalaui/libarkts'; +import { + addMemoAnnotation, + collectMemoFromCallExpression, + findCanAddMemoFromArrowFunction, + findCanAddMemoFromClassProperty, + findCanAddMemoFromMethod, + findCanAddMemoFromParameter, + findCanAddMemoFromProperty, + findCanAddMemoFromTypeAlias, +} from './utils'; + +export function findAndCollectMemoableNode(node: arkts.AstNode): arkts.AstNode { + const type = arkts.nodeType(node); + if (collectByType.has(type)) { + return collectByType.get(type)!(node); + } + return node; +} + +export class factory { + static findAndCollectMemoableProperty(node: arkts.Property): arkts.Property { + if (findCanAddMemoFromProperty(node)) { + addMemoAnnotation(node.value! as arkts.ArrowFunctionExpression); + } + return node; + } + + static findAndCollectMemoableClassProperty(node: arkts.ClassProperty): arkts.ClassProperty { + if (findCanAddMemoFromClassProperty(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { + if (findCanAddMemoFromTypeAlias(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableParameter(node: arkts.ETSParameterExpression): arkts.ETSParameterExpression { + if (findCanAddMemoFromParameter(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableMethod(node: arkts.MethodDefinition): arkts.MethodDefinition { + if (findCanAddMemoFromMethod(node)) { + addMemoAnnotation(node.scriptFunction); + } + return node; + } + + static findAndCollectMemoableArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { + if (findCanAddMemoFromArrowFunction(node)) { + addMemoAnnotation(node.scriptFunction); + } + return node; + } + + static findAndCollectMemoableCallExpression(node: arkts.CallExpression): arkts.CallExpression { + collectMemoFromCallExpression(node); + return node; + } +} + +type CollectFactoryFn = (node: any) => arkts.AstNode; + +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], +]); 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..df1f8fd4f4d04189f8054df12a56270567f775d2 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/function-collector.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 { AbstractVisitor } from '../../common/abstract-visitor'; +import { + checkIsMemoFromMemoableInfo, + collectMemoableInfoInFunctionReturnType, + collectMemoableInfoInScriptFunction, + collectMemoableInfoInVariableDeclarator, + collectMemoableInfoMapInFunctionParams, + collectMemoScriptFunctionBody, + 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 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 localInfo = collectMemoableInfoInScriptFunction(node.initializer.scriptFunction); + const shouldCollectParameter = + (localInfo.hasBuilder || localInfo.hasMemo) && !localInfo.hasMemoEntry && !localInfo.hasMemoIntrinsic; + const shouldCollectReturn = + localInfo.hasBuilder || localInfo.hasMemo || memoableInfo.hasBuilder || memoableInfo.hasMemo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.initializer.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.initializer.scriptFunction, + shouldCollectParameter + ); + if ( + !!node.initializer.scriptFunction.body && + arkts.isBlockStatement(node.initializer.scriptFunction.body) + ) { + collectMemoScriptFunctionBody( + node.initializer.scriptFunction.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.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + return node; + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + this.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + 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.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + 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..1fe12a46dd257bc80b6eeb2725499b52d8b25a9a --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/utils.ts @@ -0,0 +1,842 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; +}; + +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; +} + +export function collectMemoableInfoInTypeReference(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSTypeReference(node) || !node.part || !arkts.isETSTypeReferencePart(node.part)) { + return currInfo; + } + 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)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + 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), + }; + } + 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 || !arkts.isMethodDefinition(decl)) { + return currInfo; + } + const hasReceiver = decl.scriptFunction.hasReceiver; + const isSetter = decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + let newInfo: MemoableInfo = {}; + if (isSetter && decl.scriptFunction.params.length > 0) { + if (hasReceiver && decl.scriptFunction.params.length === 2) { + newInfo = collectMemoableInfoInParameter(decl.scriptFunction.params.at(1)!); + } else { + newInfo = collectMemoableInfoInParameter(decl.scriptFunction.params.at(0)!); + } + } else if (isGetter) { + newInfo = collectMemoableInfoInFunctionReturnType(decl.scriptFunction); + } + currInfo = { ...currInfo, ...collectMemoableInfoInScriptFunction(decl.scriptFunction), ...newInfo }; + currInfo.hasProperType = false; + if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { + 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 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 returnMemoableInfo = collectMemoableInfoInFunctionReturnType(arrowFunc.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(arrowFunc.scriptFunction); + if (!!arrowFunc.scriptFunction.body && arkts.isBlockStatement(arrowFunc.scriptFunction.body)) { + collectMemoScriptFunctionBody( + arrowFunc.scriptFunction.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); + } + return !!memoableInfo.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); + } + return ( + (!!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam) && + !memoableInfo.hasMemo && + !!memoableInfo.hasProperType + ); +} + +/** + * 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 returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.scriptFunction, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemoReturnType = checkIsMemoFromMemoableInfo(returnMemoableInfo); + if (isMemoReturnType) { + arkts.NodeCache.getInstance().collect(node.scriptFunction.returnTypeAnnotation!); + } + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + arkts.NodeCache.getInstance().collect(node, { hasMemoEntry, hasMemoIntrinsic }); + if (!!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + node.scriptFunction.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 returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.scriptFunction, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + const isMemoReturnType = checkIsMemoFromMemoableInfo(returnMemoableInfo); + if (isMemoReturnType) { + arkts.NodeCache.getInstance().collect(node.scriptFunction.returnTypeAnnotation!); + } + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + const metadata = collectMetadataInMethod(node); + arkts.NodeCache.getInstance().collect(node, { + ...metadata, + hasMemoEntry, + hasMemoIntrinsic, + }); + if (!!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + node.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * 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 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 returnMemoableInfo = collectMemoableInfoInFunctionReturnType(arg.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(arg.scriptFunction); + if (!!arg.scriptFunction.body && arkts.isBlockStatement(arg.scriptFunction.body)) { + collectMemoScriptFunctionBody( + arg.scriptFunction.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 771442543c1b92657853ff51a1ce076963d73781..59016f2fd2f84d36b1bdb5df737e15e5f30c3004 100644 --- a/arkui-plugins/common/arkts-utils.ts +++ b/arkui-plugins/common/arkts-utils.ts @@ -14,6 +14,30 @@ */ import * as arkts from '@koalaui/libarkts'; +import { DeclarationCollector } from './declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, DecoratorNames } from './predefines'; + + +/** + * 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 annotation(name: string): arkts.AnnotationUsage { const ident: arkts.Identifier = arkts.factory.createIdentifier(name).setAnnotationUsage(); @@ -29,6 +53,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,20 +126,48 @@ 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}`); + } + + const copy = [...arr]; + const [item] = copy.splice(idx, 1); + return [item, ...copy]; +} + +/** + * 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..4675725602d7f27589789677fa841ae3ad924ca7 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]; } 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 f0ad9ccb4f64d3b43318021289ab0c3e72c2ffa4..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,54 @@ 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; } export type PluginHandlerFunction = () => void; diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 08ce2babef2ddd654849e8a81987e944fb07e2f7..87bdd941348238e34a500f4bb95d6523babd0a68 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -24,67 +24,262 @@ export const EXTERNAL_SOURCE_PREFIX_NAMES: (string | RegExp)[] = [ /@arkts\..*/, /@ohos\.(?!arkui).*/, /@system\..*/, - /arkui\.(?![Uu]serView$)[A-Z]/, // temporary solution /ability\..*/, ]; -export const ARKUI_COMPONENT_IMPORT_NAME: string = '@ohos.arkui.component'; +export const EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK: (string | RegExp)[] = [ + 'std', + 'escompat', + /@arkts\..*/ +]; -export const ARKUI_STATEMANAGEMENT_IMPORT_NAME: string = '@ohos.arkui.stateManagement'; +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 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 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 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 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', + WATCH = 'Watch', + BUILDER_PARAM = 'BuilderParam', + BUILDER = 'Builder', + CUSTOM_DIALOG = 'CustomDialog', + LOCAL_STORAGE_PROP = 'LocalStorageProp', + LOCAL_STORAGE_LINK = 'LocalStorageLink', + REUSABLE = 'Reusable', + TRACK = 'Track', + JSONSTRINGIFYIGNORE = 'JSONStringifyIgnore', + JSONRENAME = 'JSONRename', + ANIMATABLE_EXTEND = 'AnimatableExtend' +} -export const IMPORT_SOURCE_MAP: Map> = new Map>([ - ['@ohos.arkui.component', new Set(['$r', '$rawfile', '_r', '_rawfile'])], +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', + 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_STORAGE_PROP_REF = 'makeStoragePropRef', + 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', +} + +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 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.STORAGE_LINK, StateManagementTypes.STORAGE_LINK_DECORATED], + [DecoratorNames.STORAGE_PROP, StateManagementTypes.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], +]); + +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.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_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]], [ - '@ohos.arkui.stateManagement', - new Set([ - 'State', - 'Prop', - 'Provide', - 'Consume', - 'StorageLink', - 'StorageProp', - 'LocalStorageLink', - 'LocalStorageProp', - 'Watch', - 'ObjectLink', - 'StateDecoratedVariable', - 'MutableState', - 'contextLocalStateOf', - 'contextLocal', - 'observableProxy', - 'SyncedProperty', - 'objectLinkState', - 'propState', - 'AppStorageLinkState', - 'StorageLinkState', - 'DecoratedV1VariableBase', - 'LinkDecoratedVariable', - 'PropDecoratedVariable', - 'StorageLinkDecoratedVariable', - 'StoragePropDecoratedVariable', - 'memo', - '__memo_context_type', - '__memo_id_type', - ]), + 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.ANIMATABLE_EXTEND, [AnimationNames.ANIMATABLE_ARITHMETIC]] ]); -export const OUTPUT_DEPENDENCY_MAP: Map = new Map([ - ['$r', ['_r']], - ['$rawfile', ['_rawfile']], - ['State', ['StateDecoratedVariable']], - ['Link', ['LinkDecoratedVariable', 'DecoratedV1VariableBase']], - ['Prop', ['PropDecoratedVariable']], - ['Provide', ['MutableState', 'contextLocalStateOf', 'observableProxy']], - ['Consume', ['MutableState', 'contextLocal', 'observableProxy']], - ['StorageProp', ['StoragePropDecoratedVariable']], - ['StorageLink', ['StorageLinkDecoratedVariable']], - ['LocalStorageLink', ['StorageLinkState', 'MutableState', 'observableProxy']], - ['LocalStorageProp', ['StorageLinkState', 'MutableState', 'observableProxy', 'propState']], - ['ObjectLink', ['objectLinkState', 'observableProxy', 'SyncedProperty']], +/** + * @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'], + [AnimationNames.ANIMATABLE_ARITHMETIC, 'arkui.component.common'] ]); + +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 e50bcbe0816e13bbe1b47891c6e9096d4bb25367..55ac1aad41e3b936d0d1faeeb5d037b98db90c66 100644 --- a/arkui-plugins/common/program-visitor.ts +++ b/arkui-plugins/common/program-visitor.ts @@ -17,8 +17,10 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor, VisitorOptions } from './abstract-visitor'; import { matchPrefix } from './arkts-utils'; import { debugDump, getDumpFileName } from './debug'; -import { ARKUI_COMPONENT_IMPORT_NAME } from './predefines'; +import { InteroperAbilityNames } from '../ui-plugins/interop/predefines'; import { PluginContext } from './plugin-context'; +import { LegacyTransformer } from '../ui-plugins/interop/legacy-transformer'; +import { ComponentTransformer } from '../ui-plugins/component-transformer'; export interface ProgramVisitorOptions extends VisitorOptions { pluginName: string; @@ -57,23 +59,8 @@ function flattenVisitorsInHooks( ]; } -function sortExternalSources(externalSources: arkts.ExternalSource[]): arkts.ExternalSource[] { - return externalSources.sort((a, b) => { - const prefix = ARKUI_COMPONENT_IMPORT_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 { @@ -84,6 +71,8 @@ export class ProgramVisitor extends AbstractVisitor { private readonly hooks?: ProgramHooks; private filenames: Map; private pluginContext?: PluginContext; + private legacyModuleList: string[] = []; + private legacyStructMap: Map; constructor(options: ProgramVisitorOptions) { super(options); @@ -94,64 +83,123 @@ export class ProgramVisitor extends AbstractVisitor { this.hooks = options.hooks; this.filenames = new Map(); this.pluginContext = options.pluginContext; + this.legacyModuleList = []; + this.legacyStructMap = new Map(); } reset(): void { super.reset(); this.filenames = new Map(); + this.legacyStructMap = new Map(); + this.legacyModuleList = []; } - programVisitor(program: arkts.Program): arkts.Program { - const skipPrefixes: (string | RegExp)[] = this.skipPrefixNames; + private getLegacyModule(): void { + const moduleList = this.pluginContext?.getProjectConfig()?.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.legacyStructMap.has(moduleName)) { + this.legacyStructMap.set(moduleName, {}); + this.legacyModuleList.push(moduleName); + } + } + } - const visited = new Set(); - const queue: arkts.Program[] = [program]; + private dumpExternalSource( + script: arkts.AstNode, + name: string, + cachePath: string | undefined, + prefixName: string, + extensionName: string + ): void { + debugDump( + script.dumpSrc(), + getDumpFileName(this.state, prefixName, undefined, name), + true, + cachePath, + extensionName + ); + } + + 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[] = 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; - debugDump( - currProgram.astNode.dumpSrc(), - getDumpFileName(this.state, 'ORI', undefined, name), - true, - cachePath, - program.programFileNameWithExtension - ); - const script = this.visitor(currProgram.astNode, currProgram, name); - if (script) { - debugDump( - script.dumpSrc(), - getDumpFileName(this.state, this.pluginName, undefined, name), - true, - cachePath, - program.programFileNameWithExtension - ); + if (this.legacyModuleList && matchPrefix(this.legacyModuleList, name)) { + this.visitLegacyInExternalSource(currProgram, name); + } else { + this.visitNonLegacyInExternalSource(program, currProgram, name, cachePath); } } - visited.add(currProgram.peer); - - for (const externalSource of sortExternalSources(currProgram.externalSources)) { - // TODO: this is very time-consuming... - 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); } } + } + + programVisitor(program: arkts.Program): arkts.Program { + this.visitExternalSources(program, [program]); let programScript = program.astNode; programScript = this.visitor(programScript, program, this.externalSourceName); @@ -162,6 +210,38 @@ export class ProgramVisitor extends AbstractVisitor { return program; } + private preVisitor( + hook: ProgramHookLifeCycle | undefined, + node: arkts.AstNode, + program?: arkts.Program, + externalSourceName?: string + ): void { + let script: arkts.EtsScript = node as arkts.EtsScript; + const preVisitors = hook?.pre?.visitors ?? []; + for (const transformer of preVisitors) { + this.visitTransformer(transformer, script, externalSourceName, program); + if (!this.hooks?.external?.pre?.resetAfter) { + transformer.reset(); + } + } + } + + private postVisitor( + hook: ProgramHookLifeCycle | undefined, + node: arkts.AstNode, + program?: arkts.Program, + externalSourceName?: string + ): void { + let script: arkts.EtsScript = node as arkts.EtsScript; + const postVisitors = hook?.post?.visitors ?? []; + for (const transformer of postVisitors) { + this.visitTransformer(transformer, script, externalSourceName, program); + if (!this.hooks?.external?.pre?.resetAfter) { + transformer.reset(); + } + } + } + visitor(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): arkts.EtsScript { let hook: ProgramHookLifeCycle | undefined; @@ -171,20 +251,13 @@ export class ProgramVisitor extends AbstractVisitor { // pre-run visitors hook = isExternal ? this.hooks?.external : this.hooks?.source; - const preVisitors = hook?.pre?.visitors ?? []; - for (const transformer of preVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); - if (!this.hooks?.external?.pre?.resetAfter) transformer.reset(); - } + this.preVisitor(hook, node, program, externalSourceName); for (const transformer of this.visitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - script = transformer.visitor(script) as arkts.EtsScript; + if (this.legacyStructMap.size > 0 && transformer instanceof ComponentTransformer) { + transformer.registerMap(this.legacyStructMap); + } + this.visitTransformer(transformer, script, externalSourceName, program); transformer.reset(); arkts.setAllParents(script); if (!transformer.isExternal) { @@ -193,7 +266,7 @@ 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; } @@ -201,15 +274,30 @@ export class ProgramVisitor extends AbstractVisitor { // post-run visitors hook = isExternal ? this.hooks?.external : this.hooks?.source; - const postVisitors = hook?.post?.visitors ?? []; - for (const transformer of postVisitors) { - transformer.isExternal = isExternal; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(script); - if (!this.hooks?.external?.pre?.resetAfter) transformer.reset(); - } - + this.postVisitor(hook, node, program, externalSourceName); return script; } + + 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..899ef0c39b3dccec8dbbbe1fb68d6243a009dcc2 --- /dev/null +++ b/arkui-plugins/common/safe-types.ts @@ -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 * 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; \ No newline at end of file diff --git a/arkui-plugins/interop-plugins/arkuiImportList.ts b/arkui-plugins/interop-plugins/arkuiImportList.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddc19b2bd98bf3d005f43c654945be7fbc640c2f --- /dev/null +++ b/arkui-plugins/interop-plugins/arkuiImportList.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. + */ + +export const ARKUI_DECLARE_LIST: Set = new Set([ + 'AbilityComponent', + 'AlphabetIndexer', + 'AnalogClock', + 'Animator', + 'Badge', + 'Blank', + 'Button', + 'Calendar', + 'CalendarPicker', + 'Camera', + 'Canvas', + 'Checkbox', + 'CheckboxGroup', + 'Circle', + 'ColorPicker', + 'ColorPickerDialog', + 'Column', + 'ColumnSplit', + 'ContentSlot', + 'Counter', + 'DataPanel', + 'DatePicker', + 'Divider', + 'EffectComponent', + 'Ellipse', + 'EmbeddedComponent', + 'Flex', + 'FolderStack', + 'FormComponent', + 'FormLink', + 'Gauge', + 'GeometryView', + 'Grid', + 'GridItem', + 'GridContainer', + 'Hyperlink', + 'Image', + 'ImageAnimator', + 'Line', + 'LinearIndicator', + 'List', + 'ListItem', + 'ListItemGroup', + 'LoadingProgress', + 'Marquee', + 'MediaCachedImage', + 'Menu', + 'MenuItem', + 'MenuItemGroup', + 'MovingPhotoView', + 'NavDestination', + 'NavRouter', + 'Navigation', + 'Navigator', + 'NodeContainer', + 'Option', + 'PageTransitionEnter', + 'PageTransitionExit', + 'Panel', + 'Particle', + 'Path', + 'PatternLock', + 'Piece', + 'PlatformView', + 'PluginComponent', + 'Polygon', + 'Polyline', + 'Progress', + 'QRCode', + 'Radio', + 'Rating', + 'Rect', + 'Refresh', + 'RelativeContainer', + 'RemoteWindow', + 'RootScene', + 'Row', + 'RowSplit', + 'RichText', + 'Screen', + 'Scroll', + 'ScrollBar', + 'Search', + 'Section', + 'Select', + 'Shape', + 'Sheet', + 'SideBarContainer', + 'Slider', + 'Span', + 'Stack', + 'Stepper', + 'StepperItem', + 'Swiper', + 'SymbolGlyph', + 'SymbolSpan', + 'TabContent', + 'Tabs', + 'Text', + 'TextPicker', + 'TextClock', + 'TextArea', + 'TextInput', + 'TextTimer', + 'TimePicker', + 'Toggle', + 'Video', + 'Web', + 'WindowScene', + 'WithTheme', + 'XComponent', + 'GridRow', + 'GridCol', + 'WaterFlow', + 'FlowItem', + 'ImageSpan', + 'LocationButton', + 'PasteButton', + 'SaveButton', + 'UIExtensionComponent', + 'IsolatedComponent', + 'RichEditor', + 'Component3D', + 'ContainerSpan', + 'Require', + 'BuilderParam', + 'Local', + 'Param', + 'Once', + 'Event', + 'State', + 'Track', + 'Trace', + 'Prop', + 'Link', + 'ObjectLink', + 'Provide', + 'Provider', + 'Consume', + 'Consumer', + 'StorageProp', + 'StorageLink', + 'Watch', + 'LocalStorageLink', + 'LocalStorageProp', + 'Component', + 'ComponentV2', + 'Entry', + 'Observed', + 'ObservedV2', + 'Preview', + 'CustomDialog', + 'Reusable', + 'Computed', + 'Builder', + 'LocalBuilder', + 'Styles', + 'Extend', + 'AnimatableExtend' +]); \ 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 57cf9906d1903e32f32008095162c5fd531c7fe0..58d5a6a6dcad76f8c71bb8cdd6aa3ea2aafcdade 100644 --- a/arkui-plugins/interop-plugins/decl_transformer.ts +++ b/arkui-plugins/interop-plugins/decl_transformer.ts @@ -16,7 +16,7 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../common/abstract-visitor'; - +import { ARKUI_DECLARE_LIST } from './arkuiImportList'; import { debugLog } from '../common/debug'; export class DeclTransformer extends AbstractVisitor { @@ -55,7 +55,6 @@ export class DeclTransformer extends AbstractVisitor { if (arkts.isEtsScript(astNode)) { astNode = this.transformImportDecl(astNode); } - const node = this.visitEachChild(astNode); if (arkts.isStructDeclaration(node)) { debugLog(`DeclTransformer:before:flag:${arkts.classDefinitionIsFromStructConst(node.definition!)}`); @@ -63,27 +62,73 @@ export class DeclTransformer extends AbstractVisitor { let newnode = this.processComponent(node); debugLog(`DeclTransformer:after:flag:${arkts.classDefinitionIsFromStructConst(newnode.definition!)}`); return newnode; + } else if (arkts.isETSImportDeclaration(astNode)) { + return this.updateImportDeclaration(astNode); + } else if (arkts.isMethodDefinition(astNode)) { + if (astNode.name?.name === 'build' ) { + return this.transformMethodDefinition(astNode); + } + return astNode; } return node; } - transformImportDecl(estNode: arkts.AstNode): arkts.AstNode { - if (!arkts.isEtsScript(estNode)) { - return estNode; + transformImportDecl(astNode: arkts.AstNode):arkts.AstNode { + if (!arkts.isEtsScript(astNode)) { + return astNode; } + let statements = astNode.statements.filter(node => this.isImportDeclarationNeedFilter(node)); + return arkts.factory.updateEtsScript(astNode, statements); + } - let statements = estNode.statements - .filter((node) => this.isImportDeclarationNeedFilter(node)) - .map((node) => this.updateImportDeclaration(node)); + transformMethodDefinition(node: arkts.MethodDefinition): arkts.AstNode { + const func: arkts.ScriptFunction = node.scriptFunction; + const isFunctionCall: boolean = false; + const typeNode: arkts.TypeNode | undefined = node.scriptFunction?.returnTypeAnnotation; + const updateFunc = arkts.factory.updateScriptFunction( + func, + !!func.body && arkts.isBlockStatement(func.body) + ? arkts.factory.updateBlock( + func.body, + func.body.statements.filter((st) => false) + ) + : undefined, + arkts.FunctionSignature.createFunctionSignature(func.typeParams, func.params, func.returnTypeAnnotation, false), + func?.flags, + func?.modifiers + ); - return arkts.factory.updateEtsScript(estNode, statements); + return arkts.factory.updateMethodDefinition( + node, + node.kind, + arkts.factory.updateIdentifier( + node.name, + node.name?.name + ), + updateFunc, + node.modifiers, + false + ); } - - isImportDeclarationNeedFilter(astNode: arkts.AstNode): boolean { - return !arkts.isETSImportDeclaration(astNode); + + isImportDeclarationNeedFilter(astNode: arkts.AstNode):boolean { + if (!arkts.isETSImportDeclaration(astNode)) { + return true; + } + return astNode?.source?.str !== '@global.arkui'; } - updateImportDeclaration(astNode: arkts.AstNode): arkts.AstNode { + updateImportDeclaration(astNode: arkts.AstNode):arkts.AstNode { + if (!arkts.isETSImportDeclaration(astNode) || astNode?.source?.str !== '@ohos.arkui.component') { + return astNode; + } + astNode.specifiers.forEach((element) => { + if (arkts.isImportSpecifier(element)) { + if (ARKUI_DECLARE_LIST.has(element.imported?.name as string)) { + arkts.ImportSpecifierSetRemovable(element); + } + } + }); return astNode; } } diff --git a/arkui-plugins/interop-plugins/index.ts b/arkui-plugins/interop-plugins/index.ts index bac3e903b252b1316a79537d0eb5bf57ab2a3888..21c971ce0e3f65a6109399b782c0ea1b39998240 100644 --- a/arkui-plugins/interop-plugins/index.ts +++ b/arkui-plugins/interop-plugins/index.ts @@ -13,8 +13,87 @@ * limitations under the License. */ -import { interopPlugin } from './interop_plugin'; +import * as arkts from '@koalaui/libarkts'; -export function interopTransform() { - return interopPlugin; +import { DeclTransformer } from './decl_transformer'; +import { EmitTransformer } from './emit_transformer'; + +import { ProgramVisitor } from '../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../common/predefines'; +import { debugLog } from '../common/debug'; +import { PluginContext, Plugins } from 'common/plugin-context'; + +export function interopTransform():Plugins { + return { + name: 'interop-plugin', + parsed: parsedTransform, + checked: checkedTransform, + clean() { + arkts.arktsGlobal.clearContext(); + }, + }; +} + +function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + debugLog('interopTransform:parsed'); + const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.astNode; + + if (script) { + const declTransformer = new DeclTransformer({ + arkui: '@koalaui.arkts-arkui.StructParse' as interop.TransfromerName + }); + + const programVisitor = new ProgramVisitor({ + pluginName: interopTransform().name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + visitors: [declTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this as unknown as PluginContext + }); + + program = programVisitor.programVisitor(program); + script = program.astNode; + this.setArkTSAst(script); + debugLog('interopTransform:parsed exit'); + return script; + } + } + debugLog('interopTransform: parsed exit with no transform'); + return script; +} + +function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + debugLog('interopTransform:checked'); + const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.astNode; + if (script) { + const emitTransformer = new EmitTransformer({ + arkui: '@koalaui.arkts-arkui.EmitBase' as interop.TransfromerName + }); + + const programVisitor = new ProgramVisitor({ + pluginName: interopTransform().name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [emitTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this as unknown as PluginContext + }); + + program = programVisitor.programVisitor(program); + script = program.astNode; + arkts.recheckSubtree(script); + this.setArkTSAst(script); + debugLog('interopTransform:checked exit'); + return script; + } + } + debugLog('interopTransform:checked exit with no transform'); + return script; } diff --git a/arkui-plugins/interop-plugins/interop_plugin.ts b/arkui-plugins/interop-plugins/interop_plugin.ts deleted file mode 100644 index e0ef5ddec363f2c3d7798bb9687318dc94f414f2..0000000000000000000000000000000000000000 --- a/arkui-plugins/interop-plugins/interop_plugin.ts +++ /dev/null @@ -1,86 +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 { DeclTransformer } from './decl_transformer'; -import { EmitTransformer } from './emit_transformer'; -import { ProgramVisitor } from '../common/program-visitor'; -import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../common/predefines'; - -import { debugLog } from '../common/debug'; - -export const interopPlugin: interop.Plugin = { - name: 'interop-plugin', - parsed(this: interop.PluginContext) { - debugLog('interopTransform:parsed'); - let node = this.getArkTSAst(); - - if (node) { - debugLog('interopTransform:parsed:before:source: ', node.dumpSrc()); - debugLog('interopTransform:parsed:before:ast: ', node.dumpJson()); - - let script: arkts.EtsScript = node as arkts.EtsScript; - - const declTransformer = new DeclTransformer({ - arkui: '@koalaui.arkts-arkui.StructParse' as interop.TransfromerName, - }); - - const programVisitor = new ProgramVisitor({ - pluginName: 'decl', - state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, - visitors: [declTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, - }); - - script = programVisitor.visitor(script); - - debugLog('interopTransform:parsed:after:source: ', script.dumpSrc()); - debugLog('interopTransform:parsed:after:ast: ', script.dumpJson()); - - this.setArkTSAst(script); - return script as interop.EtsScript; - } - }, - checked(this: interop.PluginContext) { - debugLog('interopTransform:checked'); - let node = this.getArkTSAst(); - if (node) { - debugLog('interopTransform:checked:before:source: ', node.dumpSrc()); - debugLog('interopTransform:parsed:before:ast: ', node.dumpJson()); - - let script: arkts.EtsScript = node as arkts.EtsScript; - - const emitTransformer = new EmitTransformer({ - arkui: '@koalaui.arkts-arkui.EmitBase' as interop.TransfromerName, - }); - - const programVisitor = new ProgramVisitor({ - pluginName: 'emit', - state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, - visitors: [emitTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, - }); - - // script = programVisitor.visitor(script); - - debugLog('interopTransform:checked:after:source: ', script.dumpSrc()); - debugLog('interopTransform:checked:after:ast: ', script.dumpJson()); - - this.setArkTSAst(script); - return script as interop.EtsScript; - } - }, -}; diff --git a/arkui-plugins/jest-test.config.js b/arkui-plugins/jest-test.config.js new file mode 100644 index 0000000000000000000000000000000000000000..cbca1f13e2e3c5cf964642ba71a4c025e339e706 --- /dev/null +++ b/arkui-plugins/jest-test.config.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const path = require('path'); + +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', + transform: { + '^.+\\.ts$': ['ts-jest'], + }, + testRegex: './test/ut/.+\\.test\\.ts$', + moduleFileExtensions: ['ts', 'js', 'json', 'node'], + coverageDirectory: './test/report', + collectCoverageFrom: [ + 'collectors/**', + 'common/**', + 'memo-plugins/**', + 'ui-plugins/**' + ], + coveragePathIgnorePatterns: [ + 'common/debug.ts', + 'common/etsglobal-remover.ts', + 'common/print-visitor.ts', + 'common/plugin-context.ts', + 'memo-plugins/index.ts', + 'memo-plugins/import-transformer.ts', + 'memo-plugins/memo-transformer.ts', + 'ui-plugins/index.ts', + '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', + ], + 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 1d9b5a3cf17d6e07db94b4dbbf1c779a152cba07..4f2c55f1f4c572c8cabfa2ede178bc0d0cc1018d 100644 --- a/arkui-plugins/memo-plugins/function-transformer.ts +++ b/arkui-plugins/memo-plugins/function-transformer.ts @@ -17,117 +17,57 @@ import * as arkts from '@koalaui/libarkts'; import { factory } from './memo-factory'; import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; import { + MemoInfo, PositionalIdTracker, + ReturnTypeInfo, + buildReturnTypeInfo, castArrowFunctionExpression, - castFunctionExpression, + castIdentifier, castOverloadsToMethods, castParameters, findMemoFromTypeAnnotation, + findThisAttribute, + getDeclResolveAlias, hasMemoAnnotation, + hasMemoEntryAnnotation, hasMemoIntrinsicAnnotation, hasMemoStableAnnotation, + isDeclaredMethodWithMemoParams, + isFunctionProperty, isMemoArrowFunction, isMemoClassProperty, + isMemoDeclaredClassProperty, + isMemoDeclaredIdentifier, + isMemoDeclaredMethod, + isMemoETSParameterExpression, isMemoMethodDefinition, + isMemoProperty, isMemoTSTypeAliasDeclaration, + isMemoThisAttribute, + isMemoVariableDeclarator, isStandaloneArrowFunction, - isVoidType, + isThisAttributeAssignment, removeMemoAnnotation, + 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'; -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]) - ); +interface ScopeInfo extends MemoInfo { + regardAsSameScope?: boolean; } -function updateFunctionBody( - node: arkts.BlockStatement, - parameters: arkts.ETSParameterExpression[], - returnTypeAnnotation: arkts.TypeNode | undefined, - stableThis: boolean, - hash: arkts.NumberLiteral | arkts.StringLiteral -): [ - arkts.BlockStatement, - arkts.VariableDeclaration | undefined, - arkts.ReturnStatement | arkts.BlockStatement | undefined -] { - let returnTypeAnno = - !returnTypeAnnotation || stableThis - ? arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) - : returnTypeAnnotation; - const scopeDeclaration = factory.createScopeDeclaration(returnTypeAnno, hash, parameters.length); - const memoParameters = parameters.map((name, id) => { - return factory.createMemoParameterDeclarator(id, name.identifier.name); - }); - const memoParametersDeclaration = memoParameters.length - ? [ - arkts.factory.createVariableDeclaration( - 0, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, - memoParameters - ), - ] - : []; - const syntheticReturnStatement = factory.createSyntheticReturnStatement(stableThis); - const isVoidValue = isVoidType(returnTypeAnno); - const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement(syntheticReturnStatement, isVoidValue); - if (node) { - return [ - arkts.factory.updateBlock(node, [ - scopeDeclaration, - ...memoParametersDeclaration, - unchangedCheck, - ...node.statements, - ...(mayAddLastReturn(node) ? [arkts.factory.createReturnStatement()] : []), - ]), - memoParametersDeclaration.length ? memoParametersDeclaration[0] : undefined, - syntheticReturnStatement, - ]; - } else { - return [ - node, - memoParametersDeclaration.length ? memoParametersDeclaration[0] : undefined, - syntheticReturnStatement, - ]; - } -} - -function updateMemoTypeAnnotation(typeAnnotation: arkts.AstNode | undefined): arkts.TypeNode | undefined { - if (!typeAnnotation) return undefined; - if (!arkts.isTypeNode(typeAnnotation)) return undefined; - - if (typeAnnotation && arkts.isETSFunctionType(typeAnnotation)) { - return factory.updateFunctionTypeWithMemoParameters(typeAnnotation); - } else if (typeAnnotation && arkts.isETSUnionType(typeAnnotation)) { - return arkts.factory.updateUnionType( - typeAnnotation, - typeAnnotation.types.map((it) => { - if (arkts.isETSFunctionType(it)) { - return factory.updateFunctionTypeWithMemoParameters(it); - } - return it; - }) - ); - } - return typeAnnotation; -} - -type ScopeInfo = { - name?: string; - isMemo: boolean; -}; - export interface FunctionTransformerOptions extends VisitorOptions { positionalIdTracker: PositionalIdTracker; parameterTransformer: ParameterTransformer; returnTransformer: ReturnTransformer; signatureTransformer: SignatureTransformer; + internalsTransformer?: InternalsTransformer; + useCache?: boolean; } export class FunctionTransformer extends AbstractVisitor { @@ -135,6 +75,11 @@ export class FunctionTransformer extends AbstractVisitor { private readonly parameterTransformer: ParameterTransformer; 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; constructor(options: FunctionTransformerOptions) { super(options); @@ -142,6 +87,8 @@ export class FunctionTransformer extends AbstractVisitor { this.parameterTransformer = options.parameterTransformer; this.returnTransformer = options.returnTransformer; this.signatureTransformer = options.signatureTransformer; + this.internalsTransformer = options.internalsTransformer; + this.useCache = !!options.useCache; } private scopes: ScopeInfo[] = []; @@ -151,31 +98,84 @@ export class FunctionTransformer extends AbstractVisitor { super.reset(); this.scopes = []; this.stable = 0; + this.modified = false; this.parameterTransformer.reset(); this.returnTransformer.reset(); this.signatureTransformer.reset(); } - enter(node: arkts.AstNode) { + private enterMethod(node: arkts.MethodDefinition): void { + const name = node.name.name; + const isMemo = isMemoMethodDefinition(node); + this.scopes.push({ name, isMemo }); + } + + private enterClassPropety(node: arkts.ClassProperty): void { + const name = castIdentifier(node.key).name; + const isMemo = isMemoClassProperty(node); + this.scopes.push({ name, isMemo }); + } + + private enterStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): void { + const name = undefined; + const isMemo = isMemoArrowFunction(node); + this.scopes.push({ name, isMemo }); + } + + private enterTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): void { + const name = castIdentifier(node.id).name; + const isMemo = isMemoTSTypeAliasDeclaration(node); + this.scopes.push({ name, isMemo }); + } + + private enterVariableDeclarator(node: arkts.VariableDeclarator): void { + const name = node.name.name; + const isMemo = isMemoVariableDeclarator(node); + this.scopes.push({ name, isMemo, regardAsSameScope: !!node.initializer }); + } + + private enterTSAsExpression(node: arkts.TSAsExpression): void { + const isMemo = findMemoFromTypeAnnotation(node.typeAnnotation); + this.scopes.push({ isMemo }); + } + + private enterFunctionProperty(node: arkts.Property): void { + const name = castIdentifier(node.key).name; + const isMemo = isMemoProperty(node, castArrowFunctionExpression(node.value)); + this.scopes.push({ name, isMemo }); + } + + private enterThisAttributeAssignment(node: arkts.AssignmentExpression): void { + const thisAttribute = findThisAttribute(node.left!)!; + const name = thisAttribute.name; + const isMemo = isMemoThisAttribute(thisAttribute, castArrowFunctionExpression(node.right)); + this.scopes.push({ name, isMemo }); + } + + enter(node: arkts.AstNode): this { if (arkts.isMethodDefinition(node)) { - const name = node.name.name; - const isMemo = isMemoMethodDefinition(node); - this.scopes.push({ name, isMemo }); + this.enterMethod(node); } if (arkts.isClassProperty(node) && !!node.key && arkts.isIdentifier(node.key)) { - const name = node.key.name; - const isMemo = isMemoClassProperty(node); - this.scopes.push({ name, isMemo }); + this.enterClassPropety(node); } if (isStandaloneArrowFunction(node)) { - const name = undefined; - const isMemo = isMemoArrowFunction(node); - this.scopes.push({ name, isMemo }); + this.enterStandaloneArrowFunction(node); } if (arkts.isTSTypeAliasDeclaration(node) && !!node.id && !!node.typeAnnotation) { - const name = node.id.name; - const isMemo = isMemoTSTypeAliasDeclaration(node); - this.scopes.push({ name, isMemo }); + this.enterTSTypeAliasDeclaration(node); + } + if (arkts.isVariableDeclarator(node)) { + this.enterVariableDeclarator(node); + } + if (arkts.isTSAsExpression(node) && !!node.expr && arkts.isArrowFunctionExpression(node.expr)) { + this.enterTSAsExpression(node); + } + if (isFunctionProperty(node)) { + this.enterFunctionProperty(node); + } + if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) { + this.enterThisAttributeAssignment(node); } if (arkts.isClassDefinition(node)) { if (hasMemoStableAnnotation(node)) { @@ -210,13 +210,10 @@ export class FunctionTransformer extends AbstractVisitor { } checkMemoCallInMethod(decl: arkts.MethodDefinition) { - if (this.scopes[this.scopes.length - 1].isMemo == false) { - if (this.scopes[this.scopes.length - 1].name) { - console.error( - `Attempt to call @memo-method ${decl.name.name} from non-@memo-method ${ - this.scopes[this.scopes.length - 1].name - }` - ); + const scope = this.scopes[this.scopes.length - 1]; + if (scope?.regardAsSameScope === false && scope?.isMemo === false) { + if (scope.name) { + console.error(`Attempt to call @memo-method ${decl.name.name} from non-@memo-method ${scope.name}`); throw 'Invalid @memo usage'; } else { console.error(`Attempt to call @memo-method ${decl.name.name} from anonymous non-@memo-method`); @@ -227,67 +224,95 @@ export class FunctionTransformer extends AbstractVisitor { } checkMemoCallInFunction() { - if (this.scopes[this.scopes.length - 1]?.isMemo == false) { + const scope = this.scopes[this.scopes.length - 1]; + if (scope?.regardAsSameScope === false && scope?.isMemo === false) { console.error(`Attempt to call @memo-function`); throw 'Invalid @memo usage'; } return this; } + updateInternalsInScriptFunction(scriptFunction: arkts.ScriptFunction): arkts.ScriptFunction { + if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body) || !this.internalsTransformer) { + return scriptFunction; + } + const afterInternalsTransformer = this.internalsTransformer.visitor( + scriptFunction.body + ) as arkts.BlockStatement; + return arkts.factory.updateScriptFunction( + scriptFunction, + afterInternalsTransformer, + arkts.factory.createFunctionSignature( + scriptFunction.typeParams, + scriptFunction.params, + scriptFunction.returnTypeAnnotation, + scriptFunction.hasReceiver + ), + scriptFunction.flags, + scriptFunction.modifiers + ); + } + updateScriptFunction(scriptFunction: arkts.ScriptFunction, name: string = ''): arkts.ScriptFunction { - const isStableThis = - this.stable > 0 && - scriptFunction.returnTypeAnnotation !== undefined && - arkts.isTSThisType(scriptFunction.returnTypeAnnotation); - const [body, memoParametersDeclaration, syntheticReturnStatement] = updateFunctionBody( - scriptFunction.body as arkts.BlockStatement, - castParameters(scriptFunction.params), - scriptFunction.returnTypeAnnotation, - isStableThis, - this.positionalIdTracker.id(name) + if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { + return scriptFunction; + } + if (this.parameterTransformer.isTracked(scriptFunction.body)) { + return this.updateInternalsInScriptFunction(scriptFunction); + } + if (hasMemoIntrinsicAnnotation(scriptFunction) || hasMemoEntryAnnotation(scriptFunction)) { + return this.updateInternalsInScriptFunction(scriptFunction); + } + const returnType = scriptFunction.returnTypeAnnotation; + const isStableThis = this.stable > 0 && returnType !== undefined && arkts.isTSThisType(returnType); + const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( + returnType, + findMemoFromTypeAnnotation(returnType), + isStableThis ); + const [body, parameterIdentifiers, memoParametersDeclaration, syntheticReturnStatement] = + factory.updateFunctionBody( + scriptFunction.body, + castParameters(scriptFunction.params), + returnTypeInfo, + this.positionalIdTracker.id(name) + ); const afterParameterTransformer = this.parameterTransformer - .withParameters(scriptFunction.params as arkts.ETSParameterExpression[]) + .withParameters(parameterIdentifiers) .skip(memoParametersDeclaration) .visitor(body); const afterReturnTransformer = this.returnTransformer .skip(syntheticReturnStatement) + .registerReturnTypeInfo(returnTypeInfo) .rewriteThis(this.stable > 0) .visitor(afterParameterTransformer); - const updateScriptFunction = arkts.factory.updateScriptFunction( + const updateScriptFunction = factory.updateScriptFunctionWithMemoParameters( scriptFunction, afterReturnTransformer, - arkts.FunctionSignature.createFunctionSignature( - scriptFunction.typeParams, - [ - ...factory.createHiddenParameters(), - ...scriptFunction.params, // we handle function params with signature-transformer - ], - scriptFunction.returnTypeAnnotation, - scriptFunction.hasReceiver - ), - scriptFunction.flags, - scriptFunction.modifiers + returnTypeInfo.node ); - return updateScriptFunction; + this.modified = true; + this.parameterTransformer.track(updateScriptFunction.body); + return this.updateInternalsInScriptFunction(updateScriptFunction); } private updateMethodDefinition(node: arkts.MethodDefinition): arkts.MethodDefinition { let updateMethod: arkts.MethodDefinition; const that = this; const updateOverloads = node.overloads?.map((overload) => that.visitor(overload)) ?? undefined; - if ( - node.scriptFunction.body && - (hasMemoAnnotation(node.scriptFunction) || hasMemoIntrinsicAnnotation(node.scriptFunction)) - ) { + const isMemo = + hasMemoAnnotation(node.scriptFunction) || + hasMemoIntrinsicAnnotation(node.scriptFunction) || + hasMemoEntryAnnotation(node.scriptFunction); + if (isMemo && node.scriptFunction.body) { + const hasIntrinsic = hasMemoIntrinsicAnnotation(node.scriptFunction); updateMethod = arkts.factory.updateMethodDefinition( node, node.kind, node.name, - arkts.factory.createFunctionExpression( - this.signatureTransformer.visitor( - removeMemoAnnotation(this.updateScriptFunction(node.scriptFunction, node.name.name)) - ) + this.signatureTransformer.visitor( + removeMemoAnnotation(this.updateScriptFunction(node.scriptFunction, node.name.name)), + hasIntrinsic ), node.modifiers, false @@ -297,7 +322,7 @@ export class FunctionTransformer extends AbstractVisitor { node, node.kind, node.name, - arkts.factory.createFunctionExpression(this.signatureTransformer.visitor(node.scriptFunction)), + this.signatureTransformer.visitor(node.scriptFunction), node.modifiers, false ); @@ -305,80 +330,150 @@ export class FunctionTransformer extends AbstractVisitor { if (!!updateOverloads) { updateMethod.setOverloads(castOverloadsToMethods(updateOverloads)); } + this.modified ||= this.signatureTransformer.modified; return updateMethod; } - private updateDeclaredMemoCall(node: arkts.CallExpression, decl: arkts.MethodDefinition): arkts.CallExpression { - this.checkMemoCallInMethod(decl); - const updatedArguments = node.arguments.map((it, index) => { - const type = (decl.scriptFunction.params[index] as arkts.ETSParameterExpression)?.type; - if (type && arkts.isETSFunctionType(type)) { - if ( - !hasMemoAnnotation(decl.scriptFunction.params[index] as arkts.ETSParameterExpression) && - !hasMemoIntrinsicAnnotation(decl.scriptFunction.params[index] as arkts.ETSParameterExpression) - ) { - return it; //factory.createComputeExpression(this.positionalIdTracker.id(decl.name.name), it) - } - if (arkts.isArrowFunctionExpression(it)) { - this.enterAnonymousScope(it.scriptFunction); - const res = this.updateScriptFunction(it.scriptFunction); - this.exitAnonymousScope(); - return arkts.factory.updateArrowFunction(it, res); - } + private updateDeclaredMethodMemoCall( + node: arkts.CallExpression, + decl: arkts.MethodDefinition, + ignoreSelf: boolean = false + ): arkts.CallExpression { + let updatedArguments: arkts.AstNode[] = node.arguments.map((it, index) => { + const param = decl.scriptFunction.params.at(index); + if (!param || !arkts.isEtsParameterExpression(param)) { + return it; + } + if (isMemoETSParameterExpression(param) && arkts.isArrowFunctionExpression(it)) { + this.enterAnonymousScope(it.scriptFunction); + const res = this.updateScriptFunction(it.scriptFunction); + this.exitAnonymousScope(); + this.modified = true; + return arkts.factory.updateArrowFunction(it, res); + } + return it; + }); + if (!ignoreSelf) { + this.checkMemoCallInMethod(decl); + updatedArguments = [ + ...factory.createHiddenArguments(this.positionalIdTracker.id(decl.name.name)), + ...updatedArguments, + ]; + } + const isMemo = + hasMemoAnnotation(decl.scriptFunction) || + hasMemoIntrinsicAnnotation(decl.scriptFunction) || + hasMemoEntryAnnotation(decl.scriptFunction); + if (parametrizedNodeHasReceiver(decl.scriptFunction) && isMemo) { + updatedArguments = moveToFront(updatedArguments, 2); + } + this.modified = true; + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments); + } + + private updateDeclaredCallWithName(node: arkts.CallExpression, name: string): arkts.CallExpression { + this.modified = true; + return factory.insertHiddenArgumentsToCall(node, this.positionalIdTracker.id(name)); + } + + private updateAnonymousCallWithMemoParams(node: arkts.CallExpression): arkts.CallExpression { + let newExpression: arkts.AstNode = node.expression; + if (isStandaloneArrowFunction(node.expression)) { + newExpression = arkts.factory.updateArrowFunction( + node.expression, + this.signatureTransformer.visitor(node.expression.scriptFunction) + ); + } + const that = this; + const updatedArguments: arkts.AstNode[] = node.arguments.map((it) => { + if (arkts.isArrowFunctionExpression(it) && isMemoArrowFunction(it)) { + that.enterAnonymousScope(it.scriptFunction); + const res = that.updateScriptFunction(it.scriptFunction); + that.exitAnonymousScope(); + that.modified = true; + return arkts.factory.updateArrowFunction(it, res); } return it; }); - return arkts.factory.updateCallExpression(node, node.expression, undefined, [ - ...factory.createHiddenArguments(this.positionalIdTracker.id(decl.name.name)), - ...updatedArguments, - ]); + this.modified ||= this.signatureTransformer.modified; + return arkts.factory.updateCallExpression(node, newExpression, node.typeArguments, updatedArguments); } - private udpateAnonymousMemoCall( + private updateAnonymousMemoCall( node: arkts.CallExpression, expression: arkts.ArrowFunctionExpression ): arkts.CallExpression { const scope = this.scopes[this.scopes.length - 1]; - if (!scope || !scope.isMemo || scope.name !== expression.scriptFunction.id?.name) { + const isValidScope = !!scope && scope.name === expression.scriptFunction.id?.name; + if (!isValidScope) { return node; } this.exitAnonymousScope(); + if (!scope.isMemo) { + return this.updateAnonymousCallWithMemoParams(node); + } this.checkMemoCallInFunction(); this.enterAnonymousScope(expression.scriptFunction); const res = this.updateScriptFunction(expression.scriptFunction, expression.scriptFunction.id?.name); this.exitAnonymousScope(); + const newNode = this.updateAnonymousCallWithMemoParams(node); + this.modified = true; return arkts.factory.updateCallExpression( node, arkts.factory.updateArrowFunction(expression, res), - node.typeArguments, - [...factory.createHiddenArguments(this.positionalIdTracker.id()), ...node.arguments] + newNode.typeArguments, + [...factory.createHiddenArguments(this.positionalIdTracker.id()), ...newNode.arguments] ); } + private updateCallExpressionWithNoDecl(node: arkts.CallExpression): arkts.CallExpression { + if (isStandaloneArrowFunction(node.expression)) { + return this.updateAnonymousMemoCall(node, node.expression); + } + return this.updateAnonymousCallWithMemoParams(node); + } + private updateCallExpression(node: arkts.CallExpression): arkts.CallExpression { const expr = node.expression; - const decl = arkts.getDecl(expr); + const decl = getDeclResolveAlias(expr); + if (!decl) { + return this.updateCallExpressionWithNoDecl(node); + } + if (arkts.isMethodDefinition(decl) && isMemoDeclaredMethod(decl)) { + return this.updateDeclaredMethodMemoCall(node, decl); + } + if (arkts.isMethodDefinition(decl) && isDeclaredMethodWithMemoParams(decl)) { + return this.updateDeclaredMethodMemoCall(node, decl, true); + } + if (arkts.isIdentifier(decl) && isMemoDeclaredIdentifier(decl)) { + return this.updateDeclaredCallWithName(node, decl.name); + } if ( - decl && - arkts.isMethodDefinition(decl) && - (hasMemoAnnotation(decl.scriptFunction) || hasMemoIntrinsicAnnotation(decl.scriptFunction)) + arkts.isClassProperty(decl) && + isMemoDeclaredClassProperty(decl) && + !!decl.key && + arkts.isIdentifier(decl.key) ) { - return this.updateDeclaredMemoCall(node, decl); + return this.updateDeclaredCallWithName(node, decl.key.name); } - if (isStandaloneArrowFunction(node.expression)) { - return this.udpateAnonymousMemoCall(node, node.expression); + if (arkts.isEtsParameterExpression(decl) && isMemoETSParameterExpression(decl)) { + return this.updateDeclaredCallWithName(node, decl.identifier.name); } - return node; + return this.updateCallExpressionWithNoDecl(node); } private updateClassProperty(node: arkts.ClassProperty, key: arkts.Identifier): arkts.ClassProperty { const scope = this.scopes[this.scopes.length - 1]; - if (!scope || !scope.isMemo || scope.name !== key.name) { + const isValidScope = !!scope && scope.name === key.name; + if (!isValidScope) { return node; } this.exitAnonymousScope(); + if (!scope.isMemo) { + return node; + } let res: arkts.ScriptFunction | undefined; if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { @@ -388,11 +483,12 @@ export class FunctionTransformer extends AbstractVisitor { } let typeAnnotation: arkts.TypeNode | undefined; - if (!!node.typeAnnotation && !(typeAnnotation = updateMemoTypeAnnotation(node.typeAnnotation))) { + if (!!node.typeAnnotation && !(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { console.error(`ETSFunctionType or ETSUnionType expected for @memo-property ${key.name}`); throw 'Invalid @memo usage'; } + this.modified = true; return arkts.factory.updateClassProperty( node, node.key, @@ -405,35 +501,206 @@ export class FunctionTransformer extends AbstractVisitor { private updateTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { const scope = this.scopes[this.scopes.length - 1]; - if (!scope || !scope.isMemo || scope.name !== node.id?.name) { + const isValidScope = !!scope && scope.name === node.id?.name; + if (!isValidScope) { return node; } this.exitAnonymousScope(); + if (!scope.isMemo) { + if (!!node.typeAnnotation) { + const newNode = arkts.factory.updateTSTypeAliasDeclaration( + node, + node.id, + node.typeParams, + this.signatureTransformer.visitor(node.typeAnnotation) + ); + this.modified ||= this.signatureTransformer.modified; + return newNode; + } + return node; + } let typeAnnotation: arkts.TypeNode | undefined; - if (!(typeAnnotation = updateMemoTypeAnnotation(node.typeAnnotation))) { + if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { console.error(`ETSFunctionType or ETSUnionType expected for @memo-type ${node.id!.name}`); throw 'Invalid @memo usage'; } + this.modified = true; return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, typeAnnotation); } private updateStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { const scope = this.scopes[this.scopes.length - 1]; - if (!scope || !scope.isMemo || scope.name !== node.scriptFunction.id?.name) { + const isValidScope = !!scope && scope.name === node.scriptFunction.id?.name; + if (!isValidScope) { return node; } this.exitAnonymousScope(); + if (!scope.isMemo) { + return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(node.scriptFunction)); + } this.enterAnonymousScope(node.scriptFunction); const res = this.updateScriptFunction(node.scriptFunction, node.scriptFunction.id?.name); this.exitAnonymousScope(); - return arkts.factory.updateArrowFunction(node, res); + this.modified = true; + return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(res)); + } + + private updateVariableDeclarator(node: arkts.VariableDeclarator): arkts.VariableDeclarator { + const scope = this.scopes[this.scopes.length - 1]; + const isValidScope = !!scope && scope.name === node.name.name; + if (!isValidScope) { + return node; + } + this.exitAnonymousScope(); + if (!scope.isMemo) { + if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + node.name, + arkts.factory.updateArrowFunction( + node.initializer, + this.signatureTransformer.visitor(node.initializer.scriptFunction) + ) + ); + } + return node; + } + + let typeAnnotation: arkts.TypeNode | undefined; + if ( + !!node.name.typeAnnotation && + !(typeAnnotation = factory.updateMemoTypeAnnotation(node.name.typeAnnotation)) + ) { + console.error(`ETSFunctionType or ETSUnionType expected for @memo-variable-type ${node.name.name}`); + throw 'Invalid @memo usage'; + } + + let initializer: arkts.AstNode | undefined = node.initializer; + if (!!initializer && arkts.isArrowFunctionExpression(initializer)) { + this.enterAnonymousScope(initializer.scriptFunction); + const res = this.updateScriptFunction(initializer.scriptFunction, initializer.scriptFunction.id?.name); + this.exitAnonymousScope(); + initializer = arkts.factory.updateArrowFunction(initializer, res); + } + + this.modified = true; + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier(node.name, node.name.name, typeAnnotation), + initializer + ); + } + + private updateTSAsExpression( + node: arkts.TSAsExpression, + expr: arkts.ArrowFunctionExpression + ): arkts.TSAsExpression { + const scope = this.scopes[this.scopes.length - 1]; + const isValidScope = !!scope; + if (!isValidScope) { + return node; + } + this.exitAnonymousScope(); + if (!scope.isMemo) { + return node; + } + + this.enterAnonymousScope(expr.scriptFunction); + const res = this.updateScriptFunction(expr.scriptFunction, expr.scriptFunction.id?.name); + this.exitAnonymousScope(); + + let typeAnnotation: arkts.TypeNode | undefined; + if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { + console.error(`ETSFunctionType or ETSUnionType expected for @memo-as-type`); + throw 'Invalid @memo usage'; + } + + this.modified = true; + return arkts.factory.updateTSAsExpression( + node, + arkts.factory.updateArrowFunction(expr, res), + typeAnnotation, + node.isConst + ); + } + + private updateProperty( + node: arkts.Property, + key: arkts.Identifier, + value: arkts.ArrowFunctionExpression + ): arkts.Property { + const scope = this.scopes[this.scopes.length - 1]; + const isValidScope = !!scope && scope.name === key.name; + if (!isValidScope) { + return node; + } + this.exitAnonymousScope(); + if (!scope.isMemo) { + return node; + } + + this.enterAnonymousScope(value.scriptFunction); + const res = this.updateScriptFunction(value.scriptFunction, value.scriptFunction.id?.name); + this.exitAnonymousScope(); + + this.modified = true; + return arkts.factory.updateProperty(node, key, arkts.factory.updateArrowFunction(value, res)); + } + + private updateThisAttributeAssignment( + node: arkts.AssignmentExpression, + thisAttribute: arkts.Identifier, + right: arkts.ArrowFunctionExpression + ): arkts.AssignmentExpression { + const scope = this.scopes[this.scopes.length - 1]; + const isValidScope = !!scope && scope.name === thisAttribute.name; + if (!isValidScope) { + return node; + } + this.exitAnonymousScope(); + if (!scope.isMemo) { + return node; + } + + this.enterAnonymousScope(right.scriptFunction); + const res = this.updateScriptFunction(right.scriptFunction, right.scriptFunction.id?.name); + this.exitAnonymousScope(); + + this.modified = true; + return arkts.factory.updateAssignmentExpression( + node, + node.left!, + node.operatorType, + arkts.factory.updateArrowFunction(right, res) + ); + } + + 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); @@ -452,6 +719,22 @@ export class FunctionTransformer extends AbstractVisitor { if (isStandaloneArrowFunction(node)) { return this.updateStandaloneArrowFunction(node); } + if (arkts.isVariableDeclarator(node)) { + return this.updateVariableDeclarator(node); + } + if (arkts.isTSAsExpression(node) && node.expr && arkts.isArrowFunctionExpression(node.expr)) { + return this.updateTSAsExpression(node, node.expr); + } + if (isFunctionProperty(node)) { + return this.updateProperty(node, castIdentifier(node.key), castArrowFunctionExpression(node.value)); + } + if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) { + const thisAttribute = findThisAttribute(node.left!)!; + return this.updateThisAttributeAssignment(node, thisAttribute, node.right); + } + if (arkts.isEtsScript(node) && this.modified) { + factory.createContextTypesImportDeclaration(this.program); + } return node; } } diff --git a/arkui-plugins/memo-plugins/index.ts b/arkui-plugins/memo-plugins/index.ts index 58f785dbdbf39be44dfe531cffdef9daa64e5078..5c19bc8414083b125b060e9fa01acd1f62e8bbf6 100644 --- a/arkui-plugins/memo-plugins/index.ts +++ b/arkui-plugins/memo-plugins/index.ts @@ -20,81 +20,109 @@ import { PositionalIdTracker } from './utils'; import { ReturnTransformer } from './return-transformer'; import { ParameterTransformer } from './parameter-transformer'; 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 { debugDump, debugLog, getDumpFileName } from '../common/debug'; import { SignatureTransformer } from './signature-transformer'; +import { InternalsTransformer } from './internal-transformer'; export function unmemoizeTransform(): Plugins { return { name: 'memo-plugin', - checked(this: PluginContext) { - console.log('[MEMO PLUGIN] AFTER CHECKED ENTER'); - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); - 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; - debugDump( - script.dumpSrc(), - getDumpFileName(0, 'SRC', 5, 'MEMO_AfterCheck_Begin'), - true, - cachePath, - program.programFileNameWithExtension - ); - - arkts.Performance.getInstance().createEvent('memo-checked'); - - const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); - const parameterTransformer = new ParameterTransformer({ - positionalIdTracker, - }); - const returnTransformer = new ReturnTransformer(); - const signatureTransformer = new SignatureTransformer(); - const functionTransformer = new FunctionTransformer({ - positionalIdTracker, - parameterTransformer, - returnTransformer, - signatureTransformer, - }); - - const programVisitor = new ProgramVisitor({ - pluginName: unmemoizeTransform.name, - state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, - visitors: [functionTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, - pluginContext: this, - }); - - program = programVisitor.programVisitor(program); - script = program.astNode; - - arkts.Performance.getInstance().stopEvent('memo-checked', true); - - debugLog('[AFTER MEMO SCRIPT] script: ', script.dumpSrc()); - debugDump( - script.dumpSrc(), - getDumpFileName(0, 'SRC', 6, 'MEMO_AfterCheck_End'), - true, - cachePath, - program.programFileNameWithExtension - ); - - arkts.Performance.getInstance().createEvent('memo-recheck'); - arkts.recheckSubtree(script); - arkts.Performance.getInstance().stopEvent('memo-recheck', true); - - arkts.Performance.getInstance().clearAllEvents(); - - this.setArkTSAst(script); - console.log('[MEMO PLUGIN] AFTER CHECKED EXIT'); - return script; - } - console.log('[MEMO PLUGIN] AFTER CHECKED EXIT WITH NO TRANSFORM'); - }, + checked: checkedTransform, clean() { arkts.arktsGlobal.clearContext(); }, }; } + +function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { + console.log('[MEMO PLUGIN] AFTER CHECKED ENTER'); + 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 = !isFrameworkMode && program.canSkipPhases(); + debugDump( + script.dumpSrc(), + getDumpFileName(0, 'SRC', 5, 'MEMO_AfterCheck_Begin'), + true, + cachePath, + program.fileNameWithExtension + ); + arkts.Performance.getInstance().createEvent('memo-checked'); + program = checkedProgramVisit(program, this, canSkipPhases, isFrameworkMode); + script = program.astNode; + arkts.Performance.getInstance().stopEvent('memo-checked', true); + debugLog('[AFTER MEMO SCRIPT] script: ', script.dumpSrc()); + debugDump( + script.dumpSrc(), + getDumpFileName(0, 'SRC', 6, 'MEMO_AfterCheck_End'), + true, + cachePath, + 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); + 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; + } + console.log('[MEMO PLUGIN] AFTER CHECKED EXIT WITH NO TRANSFORM'); + return undefined; +} + +function checkedProgramVisit( + program: arkts.Program, + pluginContext: PluginContext, + canSkipPhases: boolean = false, + isFrameworkMode: boolean = false +): arkts.Program { + if (canSkipPhases) { + debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); + } else { + debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName); + const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); + const parameterTransformer = new ParameterTransformer({ positionalIdTracker }); + 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: unmemoizeTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [functionTransformer], + skipPrefixNames, + pluginContext, + }); + program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); + } + return program; +} diff --git a/arkui-plugins/memo-plugins/internal-transformer.ts b/arkui-plugins/memo-plugins/internal-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..3541fb7c37fb8db20c34eb7171c37ed3cad20044 --- /dev/null +++ b/arkui-plugins/memo-plugins/internal-transformer.ts @@ -0,0 +1,49 @@ +/* + * 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 { RuntimeNames, PositionalIdTracker } from './utils'; +import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; + +export interface InternalsTransformerOptions extends VisitorOptions { + positionalIdTracker: PositionalIdTracker; +} + +export class InternalsTransformer extends AbstractVisitor { + private readonly positionalIdTracker: PositionalIdTracker; + + constructor(options: InternalsTransformerOptions) { + super(options); + this.positionalIdTracker = options.positionalIdTracker; + } + + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.isCallExpression(node)) { + if (arkts.isIdentifier(node.expression)) { + if (node.expression.name === RuntimeNames.__CONTEXT) { + return arkts.factory.createIdentifier(RuntimeNames.CONTEXT, undefined); + } + if (node.expression.name === RuntimeNames.__ID) { + return arkts.factory.createIdentifier(RuntimeNames.ID, undefined); + } + if (node.expression.name === RuntimeNames.__KEY) { + return this.positionalIdTracker.id(RuntimeNames.__KEY); + } + } + } + return node; + } +} 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..e8151cb3145fdd770f9410d1df012ba44f2cd6a5 --- /dev/null +++ b/arkui-plugins/memo-plugins/memo-cache-factory.ts @@ -0,0 +1,399 @@ +/* + * 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, + 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 hasReceiver = metadata?.hasReceiver ?? parametrizedNodeHasReceiver(node); + return factory.updateFunctionTypeWithMemoParameters(node, hasReceiver); + } + + /** + * @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); + } + 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); + 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) : 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_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 9cef81c70eb0d20ac0edecc2cad0c524f06d20d0..a9d43b59139aff263e3a3bf363c33cefdb560704 100644 --- a/arkui-plugins/memo-plugins/memo-factory.ts +++ b/arkui-plugins/memo-plugins/memo-factory.ts @@ -14,7 +14,17 @@ */ import * as arkts from '@koalaui/libarkts'; -import { RuntimeNames } from './utils'; +import { + fixGensymParams, + buildeParamInfos, + isUnmemoizedInFunctionParams, + mayAddLastReturn, + ParamInfo, + ReturnTypeInfo, + RuntimeNames, + parametrizedNodeHasReceiver, +} from './utils'; +import { moveToFront } from '../common/arkts-utils'; export class factory { // Importing @@ -30,16 +40,14 @@ export class factory { arkts.factory.createIdentifier(RuntimeNames.ID_TYPE) ); } - // TODO: Currently, import declaration can only be inserted at after-parsed stage. static createContextTypesImportDeclaration(program?: arkts.Program): void { - const source: arkts.StringLiteral = arkts.factory.createStringLiteral(RuntimeNames.CONTEXT_TYPE_DEFAULT_IMPORT); - // const resolvedSource: arkts.StringLiteral = arkts.factory.create1StringLiteral( - // arkts.ImportPathManager.create().resolvePath('', source.str) - // ); + const source: arkts.StringLiteral = arkts.factory.createStringLiteral(RuntimeNames.MEMO_IMPORT_NAME); const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( source, [factory.createContextTypeImportSpecifier(), factory.createIdTypeImportSpecifier()], - arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE + arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE, + program!, + arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE ); // Insert this import at the top of the script's statements. if (!program) { @@ -75,18 +83,53 @@ export class factory { static createHiddenParameters(): arkts.ETSParameterExpression[] { return [factory.createContextParameter(), factory.createIdParameter()]; } - static updateFunctionTypeWithMemoParameters(type: arkts.ETSFunctionType): arkts.ETSFunctionType { + static createHiddenParameterIfNotAdded( + params: readonly arkts.Expression[], + hasReceiver: boolean = false + ): readonly arkts.Expression[] { + const _params = params ?? []; + if (isUnmemoizedInFunctionParams(_params)) { + return _params; + } + let newParams: arkts.Expression[] = [...factory.createHiddenParameters(), ..._params]; + if (hasReceiver) { + newParams = moveToFront(newParams, 2); + } + return newParams; + } + static updateFunctionTypeWithMemoParameters( + type: arkts.ETSFunctionType, + hasReceiver: boolean = false + ): arkts.ETSFunctionType { return arkts.factory.updateFunctionType( type, arkts.factory.createFunctionSignature( undefined, - [...factory.createHiddenParameters(), ...type.params], + factory.createHiddenParameterIfNotAdded(type.params, hasReceiver), type.returnType, false ), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW ); } + static updateScriptFunctionWithMemoParameters( + func: arkts.ScriptFunction, + newBody?: arkts.AstNode | undefined, + returnType?: arkts.TypeNode | undefined + ): arkts.ScriptFunction { + return arkts.factory.updateScriptFunction( + func, + newBody ?? func.body, + arkts.factory.createFunctionSignature( + func.typeParams, + factory.createHiddenParameterIfNotAdded(func.params, parametrizedNodeHasReceiver(func)), + returnType ?? func.returnTypeAnnotation, + func.hasReceiver + ), + func.flags, + func.modifiers + ); + } // Arguments static createContextArgument(): arkts.AstNode { @@ -105,9 +148,16 @@ export class factory { // Memo parameters static createMemoParameterIdentifier(name: string): arkts.Identifier { + if (name === RuntimeNames.EQUAL_T) { + return arkts.factory.createIdentifier(`${RuntimeNames.PARAMETER}_${RuntimeNames.THIS}`, undefined); + } return arkts.factory.createIdentifier(`${RuntimeNames.PARAMETER}_${name}`); } static createMemoParameterDeclarator(id: number, name: string): arkts.VariableDeclarator { + const originalIdent = + name === RuntimeNames.THIS || name === RuntimeNames.EQUAL_T + ? arkts.factory.createThisExpression() + : arkts.factory.createIdentifier(name, undefined); return arkts.factory.createVariableDeclarator( arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, factory.createMemoParameterIdentifier(name), @@ -120,10 +170,19 @@ export class factory { false ), undefined, - [arkts.factory.createNumericLiteral(id), arkts.factory.createIdentifier(name)] + [arkts.factory.createNumericLiteral(id), originalIdent] ) ); } + static createMemoParameterDeclaration(parameters: string[]): arkts.VariableDeclaration { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, + parameters.map((name, id) => { + return factory.createMemoParameterDeclarator(id, name); + }) + ); + } static createMemoParameterAccess(name: string): arkts.MemberExpression { return arkts.factory.createMemberExpression( factory.createMemoParameterIdentifier(name), @@ -133,40 +192,7 @@ export class factory { false ); } - static createMemoParameterAccessMemoWithScope( - name: string, - hash: arkts.NumberLiteral | arkts.StringLiteral, - passArgs?: arkts.AstNode[] - ): arkts.CallExpression { - const updatedArgs = passArgs ? passArgs : []; - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - factory.createMemoParameterIdentifier(name), - arkts.factory.createIdentifier(RuntimeNames.VALUE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_GETTER, - false, - false - ), - undefined, - [...factory.createHiddenArguments(hash), ...updatedArgs] - ); - } - static createMemoParameterAccessMemoWithoutScope( - name: string, - hash: arkts.NumberLiteral | arkts.StringLiteral, - passArgs?: arkts.AstNode[] - ): arkts.CallExpression { - const updatedArgs = passArgs ? passArgs : []; - return arkts.factory.createCallExpression(arkts.factory.createIdentifier(name), undefined, [ - ...factory.createHiddenArguments(hash), - ...updatedArgs, - ]); - } - static createMemoParameterAccessCall( - name: string, - hash: arkts.NumberLiteral | arkts.StringLiteral, - passArgs?: arkts.AstNode[] - ): arkts.CallExpression { + static createMemoParameterAccessCall(name: string, passArgs?: arkts.AstNode[]): arkts.CallExpression { const updatedArgs = passArgs ? passArgs : []; return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( @@ -275,6 +301,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 { @@ -303,4 +341,76 @@ export class factory { [factory.createIdArgument(hash), factory.createLambdaWrapper(node)] ); } + + static updateFunctionBody( + node: arkts.BlockStatement, + parameters: arkts.ETSParameterExpression[], + returnTypeInfo: ReturnTypeInfo, + hash: arkts.NumberLiteral | arkts.StringLiteral + ): [ + arkts.BlockStatement, + ParamInfo[], + arkts.VariableDeclaration | undefined, + arkts.ReturnStatement | arkts.BlockStatement | undefined + ] { + const paramInfos = buildeParamInfos(parameters); + const gensymParamsCount = fixGensymParams(paramInfos, node); + const parameterNames = paramInfos.map((it) => it.ident.name); + const scopeDeclaration = factory.createScopeDeclaration(returnTypeInfo.node, hash, parameterNames.length); + const memoParametersDeclaration = parameterNames.length + ? factory.createMemoParameterDeclaration(parameterNames) + : undefined; + const syntheticReturnStatement = factory.createSyntheticReturnStatement(!!returnTypeInfo.isStableThis); + const isVoidValue = !!returnTypeInfo.isVoid; + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( + syntheticReturnStatement, + isVoidValue + ); + return [ + arkts.factory.updateBlock(node, [ + ...node.statements.slice(0, gensymParamsCount), + scopeDeclaration, + ...(memoParametersDeclaration ? [memoParametersDeclaration] : []), + unchangedCheck, + ...node.statements.slice(gensymParamsCount), + ...(mayAddLastReturn(node) ? [arkts.factory.createReturnStatement()] : []), + ]), + paramInfos, + memoParametersDeclaration, + syntheticReturnStatement, + ]; + } + + static updateMemoTypeAnnotation(typeAnnotation: arkts.AstNode | undefined): arkts.TypeNode | undefined { + if (!typeAnnotation || !arkts.isTypeNode(typeAnnotation)) { + return undefined; + } + + if (arkts.isETSFunctionType(typeAnnotation)) { + return factory.updateFunctionTypeWithMemoParameters(typeAnnotation); + } else if (arkts.isETSUnionType(typeAnnotation)) { + return arkts.factory.updateUnionType( + typeAnnotation, + typeAnnotation.types.map((it) => { + if (arkts.isETSFunctionType(it)) { + return factory.updateFunctionTypeWithMemoParameters(it); + } + return it; + }) + ); + } + return typeAnnotation; + } + + static insertHiddenArgumentsToCall( + node: arkts.CallExpression, + hash: arkts.NumberLiteral | arkts.StringLiteral, + hasReceiver?: boolean + ): arkts.CallExpression { + 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 757e45f77d4d2f086a33d291208d89ea48afdb16..8aef0822cd3d5292081474db178b415b6a50a612 100644 --- a/arkui-plugins/memo-plugins/parameter-transformer.ts +++ b/arkui-plugins/memo-plugins/parameter-transformer.ts @@ -17,62 +17,72 @@ import * as arkts from '@koalaui/libarkts'; import { factory } from './memo-factory'; import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; import { - hasMemoAnnotation, - hasMemoIntrinsicAnnotation, + buildReturnTypeInfo, + castParameters, + findReturnTypeFromTypeAnnotation, + isMemoETSParameterExpression, isMemoParametersDeclaration, + isUnmemoizedInFunctionParams, + MemoInfo, + ParamInfo, PositionalIdTracker, + ReturnTypeInfo, + RuntimeNames, } from './utils'; +import { ReturnTransformer } from './return-transformer'; export interface ParameterTransformerOptions extends VisitorOptions { positionalIdTracker: PositionalIdTracker; } +interface RewriteMemoInfo extends MemoInfo { + rewritePeer: number; +} + export class ParameterTransformer extends AbstractVisitor { private rewriteIdentifiers?: Map arkts.MemberExpression | arkts.Identifier>; - private rewriteCalls?: Map arkts.CallExpression>; + private rewriteCalls?: Map arkts.CallExpression>; + private rewriteMemoInfos?: Map; + private rewriteThis?: boolean; private skipNode?: arkts.VariableDeclaration; - private readonly positionalIdTracker: PositionalIdTracker; + private visited: Set; + + private positionalIdTracker: PositionalIdTracker; constructor(options: ParameterTransformerOptions) { super(options); this.positionalIdTracker = options.positionalIdTracker; + this.visited = new Set(); } reset(): void { super.reset(); this.rewriteIdentifiers = undefined; this.rewriteCalls = undefined; + this.rewriteMemoInfos = undefined; this.skipNode = undefined; + this.visited.clear(); } - withParameters(parameters: arkts.ETSParameterExpression[]): ParameterTransformer { + withThis(flag: boolean): ParameterTransformer { + this.rewriteThis = flag; + return this; + } + + withParameters(parameters: ParamInfo[]): ParameterTransformer { this.rewriteCalls = new Map( parameters - .filter((it) => it.type && (arkts.isETSFunctionType(it.type) || arkts.isETSUnionType(it.type))) + .filter( + (it) => + it.param.type && (arkts.isETSFunctionType(it.param.type) || arkts.isETSUnionType(it.param.type)) + ) .map((it) => { return [ - it.peer, - (passArgs: arkts.AstNode[]) => { - if (hasMemoAnnotation(it) || hasMemoIntrinsicAnnotation(it)) { - if (it.type && arkts.isETSFunctionType(it.type) && !it.optional) { - return factory.createMemoParameterAccessMemoWithScope( - it.identifier.name, - this.positionalIdTracker?.id(), - passArgs - ); - } else { - return factory.createMemoParameterAccessMemoWithoutScope( - it.identifier.name, - this.positionalIdTracker?.id(), - passArgs - ); - } - } - return factory.createMemoParameterAccessCall( - it.identifier.name, - this.positionalIdTracker?.id(), - passArgs - ); + it.param.identifier.name.startsWith(RuntimeNames.GENSYM) + ? it.ident.originalPeer + : it.param.originalPeer, + (passArgs: arkts.Expression[]): arkts.CallExpression => { + return factory.createMemoParameterAccessCall(it.ident.name, passArgs); }, ]; }) @@ -80,12 +90,25 @@ export class ParameterTransformer extends AbstractVisitor { this.rewriteIdentifiers = new Map( parameters.map((it) => { return [ - it.peer, - () => { - if ((it.type && arkts.isETSFunctionType(it.type)) || it.optional) { - return arkts.factory.createIdentifier(it.identifier.name); - } - return factory.createMemoParameterAccess(it.identifier.name); + it.param.identifier.name.startsWith(RuntimeNames.GENSYM) + ? it.ident.originalPeer + : it.param.originalPeer, + (): arkts.MemberExpression => { + return factory.createMemoParameterAccess(it.ident.name); + }, + ]; + }) + ); + this.rewriteMemoInfos = new Map( + parameters.map((it) => { + return [ + it.param.identifier.name.startsWith(RuntimeNames.GENSYM) + ? it.ident.originalPeer + : it.param.originalPeer, + { + name: it.param.identifier.name, + rewritePeer: it.param.identifier.originalPeer, + isMemo: isMemoETSParameterExpression(it.param), }, ]; }) @@ -98,33 +121,189 @@ export class ParameterTransformer extends AbstractVisitor { return this; } + track(node: arkts.AstNode | undefined): void { + if (!!node?.peer) { + this.visited.add(node.peer); + } + } + + isTracked(node: arkts.AstNode | undefined): boolean { + return !!node?.peer && this.visited.has(node.peer); + } + + private updateArrowFunctionFromVariableDeclareInit( + initializer: arkts.ArrowFunctionExpression, + returnType: arkts.TypeNode | undefined + ): arkts.ArrowFunctionExpression { + const scriptFunction = initializer.scriptFunction; + if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { + return initializer; + } + if (isUnmemoizedInFunctionParams(scriptFunction.params)) { + return initializer; + } + const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( + returnType ?? scriptFunction.returnTypeAnnotation, + true + ); + const [body, parameterIdentifiers, memoParametersDeclaration, syntheticReturnStatement] = + factory.updateFunctionBody( + scriptFunction.body, + castParameters(scriptFunction.params), + returnTypeInfo, + this.positionalIdTracker.id() + ); + const paramaterTransformer = new ParameterTransformer({ + positionalIdTracker: this.positionalIdTracker, + }); + const returnTransformer = new ReturnTransformer(); + const afterParameterTransformer = paramaterTransformer + .withParameters(parameterIdentifiers) + .skip(memoParametersDeclaration) + .visitor(body); + const afterReturnTransformer = returnTransformer + .skip(syntheticReturnStatement) + .registerReturnTypeInfo(returnTypeInfo) + .visitor(afterParameterTransformer); + const updateScriptFunction = factory.updateScriptFunctionWithMemoParameters( + scriptFunction, + afterReturnTransformer, + returnTypeInfo.node + ); + paramaterTransformer.reset(); + returnTransformer.reset(); + this.track(updateScriptFunction.body); + return arkts.factory.updateArrowFunction(initializer, updateScriptFunction); + } + + private updateVariableDeclareInit( + initializer: T | undefined, + returnType: arkts.TypeNode | undefined + ): T | undefined { + if (!initializer) { + return undefined; + } + if (arkts.isConditionalExpression(initializer)) { + return arkts.factory.updateConditionalExpression( + initializer, + initializer.test, + this.updateVariableDeclareInit(initializer.consequent, returnType), + this.updateVariableDeclareInit(initializer.alternate, returnType) + ) as unknown as T; + } + if (arkts.isTSAsExpression(initializer)) { + return arkts.factory.updateTSAsExpression( + initializer, + this.updateVariableDeclareInit(initializer.expr, returnType), + factory.updateMemoTypeAnnotation(initializer.typeAnnotation), + initializer.isConst + ) as unknown as T; + } + if (arkts.isArrowFunctionExpression(initializer)) { + return this.updateArrowFunctionFromVariableDeclareInit(initializer, returnType) as unknown as T; + } + return initializer; + } + + private updateParamReDeclare(node: arkts.VariableDeclarator, memoInfo: RewriteMemoInfo): arkts.VariableDeclarator { + const shouldUpdate: boolean = node.name.name !== memoInfo.name && memoInfo.isMemo; + if (!shouldUpdate) { + return node; + } + const decl = arkts.getPeerDecl(memoInfo.rewritePeer); + if (!decl || !arkts.isEtsParameterExpression(decl)) { + return node; + } + + let typeAnnotation: arkts.TypeNode | undefined; + if ( + !!node.name.typeAnnotation && + !(typeAnnotation = factory.updateMemoTypeAnnotation(node.name.typeAnnotation)) + ) { + console.error(`ETSFunctionType or ETSUnionType expected for @memo-variable-type ${node.name.name}`); + throw 'Invalid @memo usage'; + } + + const returnType = findReturnTypeFromTypeAnnotation(decl.type); + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier(node.name, node.name.name, typeAnnotation), + this.updateVariableDeclareInit(node.initializer, returnType) + ); + } + + private updateVariableReDeclarationFromParam(node: arkts.VariableDeclaration): arkts.VariableDeclaration { + const that = this; + return arkts.factory.updateVariableDeclaration( + node, + node.modifiers, + node.declarationKind, + node.declarators.map((declarator) => { + if (that.rewriteMemoInfos?.has(declarator.name.originalPeer)) { + const memoInfo = that.rewriteMemoInfos.get(declarator.name.originalPeer)!; + return that.updateParamReDeclare(declarator, memoInfo); + } + if (!!declarator.initializer && arkts.isIdentifier(declarator.initializer)) { + const decl = arkts.getPeerDecl(declarator.initializer.originalPeer); + if (decl && that.rewriteIdentifiers?.has(decl.peer)) { + return arkts.factory.updateVariableDeclarator( + declarator, + declarator.flag, + declarator.name, + that.rewriteIdentifiers.get(decl.peer)!() + ); + } + } + return declarator; + }) + ); + } + + private updateCallReDeclare( + node: arkts.CallExpression, + oriName: arkts.Identifier, + memoInfo: RewriteMemoInfo + ): arkts.CallExpression { + const shouldUpdate: boolean = oriName.name !== memoInfo.name && memoInfo.isMemo; + if (!shouldUpdate) { + return node; + } + return factory.insertHiddenArgumentsToCall(node, this.positionalIdTracker.id(oriName.name)); + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { // TODO: temporary checking skip nodes by comparison with expected skip nodes // Should be fixed when update procedure implemented properly - if (!beforeChildren) { - return beforeChildren; - } if (/* beforeChildren === this.skipNode */ isMemoParametersDeclaration(beforeChildren)) { return beforeChildren; } - if (arkts.isCallExpression(beforeChildren)) { - if (arkts.isIdentifier(beforeChildren.expression)) { - const decl = arkts.getDecl(beforeChildren.expression); - if (decl && arkts.isEtsParameterExpression(decl) && this.rewriteCalls?.has(decl.peer)) { - return this.rewriteCalls.get(decl.peer)!(beforeChildren.arguments.map((it) => this.visitor(it))); + if (arkts.isVariableDeclaration(beforeChildren)) { + return this.updateVariableReDeclarationFromParam(beforeChildren); + } + if (arkts.isCallExpression(beforeChildren) && arkts.isIdentifier(beforeChildren.expression)) { + const decl = arkts.getPeerDecl(beforeChildren.expression.originalPeer); + if (decl && this.rewriteCalls?.has(decl.peer)) { + const updateCall = this.rewriteCalls.get(decl.peer)!( + beforeChildren.arguments.map((it) => this.visitor(it) as arkts.Expression) + ); + if (this.rewriteMemoInfos?.has(decl.peer)) { + const memoInfo = this.rewriteMemoInfos.get(decl.peer)!; + return this.updateCallReDeclare(updateCall, beforeChildren.expression, memoInfo); } + return updateCall; } } const node = this.visitEachChild(beforeChildren); if (arkts.isIdentifier(node)) { - const decl = arkts.getDecl(node); - if (decl && arkts.isEtsParameterExpression(decl) && this.rewriteIdentifiers?.has(decl.peer)) { - const res = this.rewriteIdentifiers.get(decl.peer)!(); - if (arkts.isMemberExpression(res)) { - return res; - } + const decl = arkts.getPeerDecl(node.originalPeer); + if (decl && this.rewriteIdentifiers?.has(decl.peer)) { + return this.rewriteIdentifiers.get(decl.peer)!(); } } + if (arkts.isThisExpression(node) && this.rewriteThis) { + return factory.createMemoParameterAccess(RuntimeNames.THIS); + } return node; } } diff --git a/arkui-plugins/memo-plugins/return-transformer.ts b/arkui-plugins/memo-plugins/return-transformer.ts index 563d5562191c78afd9ee8dcdb328bc21cfc68cd4..c2c0fac2e6033aca90d5fce9060020b0286a4ebb 100644 --- a/arkui-plugins/memo-plugins/return-transformer.ts +++ b/arkui-plugins/memo-plugins/return-transformer.ts @@ -16,16 +16,18 @@ import * as arkts from '@koalaui/libarkts'; import { factory } from './memo-factory'; import { AbstractVisitor } from '../common/abstract-visitor'; -import { isSyntheticReturnStatement } from './utils'; +import { isSyntheticReturnStatement, ReturnTypeInfo } from './utils'; export class ReturnTransformer extends AbstractVisitor { private skipNode?: arkts.ReturnStatement | arkts.BlockStatement; private stableThis: boolean = false; + private returnTypeInfo: ReturnTypeInfo | undefined; reset() { super.reset(); this.skipNode = undefined; this.stableThis = false; + this.returnTypeInfo = undefined; } skip(syntheticReturnStatement?: arkts.ReturnStatement | arkts.BlockStatement): ReturnTransformer { @@ -38,6 +40,11 @@ export class ReturnTransformer extends AbstractVisitor { return this; } + registerReturnTypeInfo(returnTypeInfo: ReturnTypeInfo): ReturnTransformer { + this.returnTypeInfo = returnTypeInfo; + return this; + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { // TODO: temporary checking skip nodes by comparison with expected skip nodes // Should be fixed when update procedure implemented properly @@ -58,7 +65,20 @@ export class ReturnTransformer extends AbstractVisitor { node, ]); } - return arkts.factory.updateReturnStatement(node, factory.createRecacheCall(node.argument)); + + let argument = node.argument; + if ( + !!this.returnTypeInfo?.node && + this.returnTypeInfo.isMemo && + arkts.isArrowFunctionExpression(argument) + ) { + argument = arkts.factory.updateArrowFunction( + argument, + factory.updateScriptFunctionWithMemoParameters(argument.scriptFunction) + ); + } + + return arkts.factory.updateReturnStatement(node, factory.createRecacheCall(argument)); } return node; } diff --git a/arkui-plugins/memo-plugins/signature-transformer.ts b/arkui-plugins/memo-plugins/signature-transformer.ts index 985d57d5c949cb7388810cd70726a05b88225f76..b7fa8a62d37b9500e7e334a9cf66ee816279e993 100644 --- a/arkui-plugins/memo-plugins/signature-transformer.ts +++ b/arkui-plugins/memo-plugins/signature-transformer.ts @@ -15,10 +15,36 @@ import * as arkts from '@koalaui/libarkts'; import { factory } from './memo-factory'; -import { hasMemoAnnotation, hasMemoIntrinsicAnnotation } from './utils'; +import { + hasMemoAnnotation, + hasMemoIntrinsicAnnotation, + parametrizedNodeHasReceiver, + isMemoTSTypeAliasDeclaration, +} from './utils'; import { AbstractVisitor } from '../common/abstract-visitor'; +function isScriptFunctionFromGetter(node: arkts.ScriptFunction): boolean { + return ( + !!node.parent && + !!node.parent.parent && + arkts.isFunctionExpression(node.parent) && + arkts.isMethodDefinition(node.parent.parent) && + node.parent.parent.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET + ); +} + +function isScriptFunctionFromSetter(node: arkts.ScriptFunction): boolean { + return ( + !!node.parent && + !!node.parent.parent && + arkts.isFunctionExpression(node.parent) && + arkts.isMethodDefinition(node.parent.parent) && + node.parent.parent.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET + ); +} + export class SignatureTransformer extends AbstractVisitor { + /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ public modified = false; reset(): void { @@ -32,17 +58,25 @@ export class SignatureTransformer extends AbstractVisitor { if (memo) { this.modified = true; } + const isFromGetter = isScriptFunctionFromGetter(node); + const isFromSetter = isScriptFunctionFromSetter(node); + const shouldAddMemoParam = memo && !isFromGetter && !isFromSetter; + const shouldApplyMemoToParamExpr = memo && isFromSetter; + const shouldApplyMemoToReturnType = memo && isFromGetter; + const newParams = node.params.map((it) => this.visitor(it, shouldApplyMemoToParamExpr)); return arkts.factory.updateScriptFunction( node, node.body, arkts.factory.createFunctionSignature( node.typeParams, - [...(memo ? factory.createHiddenParameters() : []), ...node.params.map((it) => this.visitor(it))], + shouldAddMemoParam + ? factory.createHiddenParameterIfNotAdded(newParams, parametrizedNodeHasReceiver(node)) + : newParams, node.returnTypeAnnotation - ? this.visitor(node.returnTypeAnnotation) + ? this.visitor(node.returnTypeAnnotation, shouldApplyMemoToReturnType) : memo - ? arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) - : undefined, + ? arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + : undefined, node.hasReceiver ), node.flags, @@ -50,7 +84,7 @@ export class SignatureTransformer extends AbstractVisitor { ) as any as T; } if (arkts.isEtsParameterExpression(node)) { - const memo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node); + const memo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node) || applyMemo; if (!node.type) { if (memo) { console.error(`@memo parameter ${node.identifier.name} without type annotatation`); @@ -66,11 +100,12 @@ export class SignatureTransformer extends AbstractVisitor { if (memo) { this.modified = true; } + const newParams = node.params.map((it) => this.visitor(it)); return arkts.factory.updateFunctionType( node, arkts.factory.createFunctionSignature( undefined, - [...(memo ? factory.createHiddenParameters() : []), ...node.params.map((it) => this.visitor(it))], + memo ? factory.createHiddenParameterIfNotAdded(newParams) : newParams, this.visitor(node.returnType!), false ), @@ -86,7 +121,26 @@ export class SignatureTransformer extends AbstractVisitor { if (arkts.isETSUndefinedType(node)) { return node as any as T; } + if (arkts.isETSTypeReference(node) && applyMemo) { + if (!node.part || !node.part.name) { + console.error(`@memo parameter has no type reference`); + throw 'Invalid @memo usage'; + } + 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'; + } + const memoDecl = isMemoTSTypeAliasDeclaration(decl); + if (memoDecl) { + return node as any as T; + } + console.error(`@memo parameter type reference has no @memo type declaration`); + throw 'Invalid @memo usage'; + } if (applyMemo) { + console.error(`@memo parameter's signature has invalid type`); throw 'Invalid @memo usage'; } return node; diff --git a/arkui-plugins/memo-plugins/utils.ts b/arkui-plugins/memo-plugins/utils.ts index cae4fa8914674798cec95cf240bb51df9f37cab0..e3b415e7fa2216438caddbf64d73efd2ed1455d7 100644 --- a/arkui-plugins/memo-plugins/utils.ts +++ b/arkui-plugins/memo-plugins/utils.ts @@ -20,13 +20,18 @@ const UniqueId = common.UniqueId; export enum RuntimeNames { __CONTEXT = '__context', __ID = '__id', + __KEY = '__key', + ANNOTATION_BUILDER = 'Builder', ANNOTATION = 'memo', + ANNOTATION_ENTRY = 'memo_entry', ANNOTATION_INTRINSIC = 'memo_intrinsic', ANNOTATION_STABLE = 'memo_stable', + ANNOTATION_SKIP = 'memo_skip', COMPUTE = 'compute', CONTEXT = '__memo_context', CONTEXT_TYPE = '__memo_context_type', - CONTEXT_TYPE_DEFAULT_IMPORT = '@ohos.arkui.StateManagement.runtime', + MEMO_IMPORT_NAME = 'arkui.stateManagement.runtime', + GENSYM = 'gensym%%_', ID = '__memo_id', ID_TYPE = '__memo_id_type', INTERNAL_PARAMETER_STATE = 'param', @@ -36,7 +41,26 @@ export enum RuntimeNames { INTERNAL_VALUE_OK = 'unchanged', PARAMETER = '__memo_parameter', SCOPE = '__memo_scope', + THIS = 'this', VALUE = 'value', + EQUAL_T = '=t', +} + +export interface ReturnTypeInfo { + node: arkts.TypeNode | undefined; + isMemo?: boolean; + isVoid?: boolean; + isStableThis?: boolean; +} + +export interface ParamInfo { + ident: arkts.Identifier; + param: arkts.ETSParameterExpression; +} + +export interface MemoInfo { + name?: string; + isMemo: boolean; } function baseName(path: string): string { @@ -47,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! @@ -88,28 +121,35 @@ export type MemoAstNode = | arkts.ClassProperty | arkts.TSTypeAliasDeclaration | arkts.ETSFunctionType - | arkts.ArrowFunctionExpression; + | arkts.ArrowFunctionExpression + | arkts.ETSTypeReference + | arkts.VariableDeclaration; export function hasMemoAnnotation(node: T): boolean { - return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION)); + return node.annotations.some( + (it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION) || isMemoAnnotation(it, RuntimeNames.ANNOTATION_BUILDER) + ); } export function hasMemoIntrinsicAnnotation(node: T): boolean { return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION_INTRINSIC)); } +export function hasMemoEntryAnnotation(node: T): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION_ENTRY)); +} + export function hasMemoStableAnnotation(node: arkts.ClassDefinition): boolean { - return node.annotations.some( - (it) => it.expr !== undefined && arkts.isIdentifier(it.expr) && it.expr.name === RuntimeNames.ANNOTATION_STABLE - ); + return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION_STABLE)); +} + +export function hasMemoSkipAnnotation(node: arkts.ETSParameterExpression): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION_SKIP)); } export function removeMemoAnnotation(node: T): T { const newAnnotations: arkts.AnnotationUsage[] = node.annotations.filter( - (it) => - !isMemoAnnotation(it, RuntimeNames.ANNOTATION) && - !isMemoAnnotation(it, RuntimeNames.ANNOTATION_INTRINSIC) && - !isMemoAnnotation(it, RuntimeNames.ANNOTATION_STABLE) + (it) => !isMemoAnnotation(it, RuntimeNames.ANNOTATION) && !isMemoAnnotation(it, RuntimeNames.ANNOTATION_STABLE) ); if (arkts.isEtsParameterExpression(node)) { node.annotations = newAnnotations; @@ -216,6 +256,15 @@ export function castArrowFunctionExpression(value: arkts.Expression | undefined) return value as unknown as arkts.ArrowFunctionExpression; } +/** + * es2panda API is weird here + * + * @deprecated + */ +export function castIdentifier(value: arkts.AstNode | undefined): arkts.Identifier { + return value as unknown as arkts.Identifier; +} + /** * es2panda API is weird here * @@ -229,9 +278,65 @@ export function isStandaloneArrowFunction(node: arkts.AstNode): node is arkts.Ar if (!arkts.isArrowFunctionExpression(node)) return false; // handling anonymous arrow function call - if (arkts.isCallExpression(node.parent) && node.parent.expression.peer === node.peer) return true; + if (!!node.parent && arkts.isCallExpression(node.parent) && node.parent.expression.peer === node.peer) { + return true; + } + + return ( + !!node.parent && + !arkts.isVariableDeclarator(node.parent) && + !arkts.isClassProperty(node.parent) && + !(arkts.isCallExpression(node.parent) && node.parent.expression) + ); +} + +export function isFunctionProperty(node: arkts.AstNode): node is arkts.Property { + return ( + arkts.isProperty(node) && + !!node.key && + arkts.isIdentifier(node.key) && + !!node.value && + arkts.isArrowFunctionExpression(node.value) + ); +} + +export function isThisAttributeAssignment(node: arkts.AstNode): node is arkts.AssignmentExpression { + if (!arkts.isAssignmentExpression(node)) { + return false; + } + if (!node.left || !node.right) { + return false; + } + const isAssignOperator = node.operatorType === arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION; + const isThisAttribute = + arkts.isMemberExpression(node.left) && + arkts.isThisExpression(node.left.object) && + arkts.isIdentifier(node.left.property); + return isAssignOperator && isThisAttribute; +} - return !arkts.isClassProperty(node.parent) && !(arkts.isCallExpression(node.parent) && node.parent.expression); +export function findThisAttribute(node: arkts.AstNode): arkts.Identifier | undefined { + if (!arkts.isMemberExpression(node) || !arkts.isIdentifier(node.property)) { + return undefined; + } + return node.property; +} + +export function isMemoThisAttribute(node: arkts.Identifier, value: arkts.ArrowFunctionExpression): boolean { + let isMemo: boolean = isMemoArrowFunction(value); + if (isMemo) { + return true; + } + const decl: arkts.AstNode | undefined = getDeclResolveAlias(node); + if (!decl) { + return false; + } + if (arkts.isClassProperty(decl)) { + isMemo ||= isMemoClassProperty(decl); + } else if (arkts.isMethodDefinition(decl)) { + isMemo ||= isMemoDeclaredMethod(decl); + } + return isMemo; } export function isMemoClassProperty(node: arkts.ClassProperty): boolean { @@ -246,7 +351,11 @@ export function isMemoClassProperty(node: arkts.ClassProperty): boolean { } export function isMemoMethodDefinition(node: arkts.MethodDefinition): boolean { - return hasMemoAnnotation(node.scriptFunction) || hasMemoIntrinsicAnnotation(node.scriptFunction); + return ( + hasMemoAnnotation(node.scriptFunction) || + hasMemoIntrinsicAnnotation(node.scriptFunction) || + hasMemoEntryAnnotation(node.scriptFunction) + ); } export function isMemoArrowFunction(node: arkts.ArrowFunctionExpression): boolean { @@ -259,11 +368,110 @@ export function isMemoTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration) return isMemo; } -export function findMemoFromTypeAnnotation(typeAnnotation: arkts.TypeNode | undefined): boolean { +export function isMemoETSParameterExpression(param: arkts.ETSParameterExpression): boolean { + const type = param.identifier.typeAnnotation; + if (!type) { + return false; + } + let isMemo: boolean = hasMemoAnnotation(param) || hasMemoIntrinsicAnnotation(param); + isMemo ||= findMemoFromTypeAnnotation(type); + let decl: arkts.AstNode | undefined; + if ( + arkts.isETSTypeReference(type) && + !!type.part && + !!type.part.name && + !!(decl = getDeclResolveAlias(type.part.name)) + ) { + if (arkts.isTSTypeAliasDeclaration(decl)) { + isMemo ||= hasMemoAnnotation(decl) || hasMemoIntrinsicAnnotation(decl); + isMemo ||= findMemoFromTypeAnnotation(decl.typeAnnotation); + return isMemo; + } + } + return isMemo; +} + +export function isMemoVariableDeclaration(node: arkts.VariableDeclaration): boolean { + return hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node); +} + +export function isMemoVariableDeclarator(node: arkts.VariableDeclarator): boolean { + let isMemo: boolean = false; + if (!!node.name.typeAnnotation) { + isMemo ||= findMemoFromTypeAnnotation(node.name.typeAnnotation); + } + if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { + isMemo ||= isMemoArrowFunction(node.initializer); + } + if (!!node.parent && arkts.isVariableDeclaration(node.parent)) { + isMemo ||= isMemoVariableDeclaration(node.parent); + } + return isMemo; +} + +export function isMemoProperty(node: arkts.Property, value: arkts.ArrowFunctionExpression): boolean { + let isMemo: boolean = isMemoArrowFunction(value); + if (isMemo) { + return true; + } + let decl: arkts.AstNode | undefined; + if (!node.key || !(decl = getDeclResolveAlias(node.key))) { + return false; + } + if (arkts.isMethodDefinition(decl)) { + isMemo ||= isMemoDeclaredMethod(decl); + } + return isMemo; +} + +export function isMemoDeclaredMethod(decl: arkts.MethodDefinition): boolean { + if ( + decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET && + findMemoFromTypeAnnotation(decl.scriptFunction.returnTypeAnnotation) + ) { + return true; + } + return !hasMemoEntryAnnotation(decl.scriptFunction) && isMemoMethodDefinition(decl); +} + +export function isDeclaredMethodWithMemoParams(decl: arkts.MethodDefinition): boolean { + if (decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { + return false; + } + return decl.scriptFunction.params.some((param) => { + return arkts.isEtsParameterExpression(param) && isMemoETSParameterExpression(param); + }); +} + +export function isMemoDeclaredIdentifier(decl: arkts.Identifier): boolean { + if (findMemoFromTypeAnnotation(decl.typeAnnotation)) { + return true; + } + if (!!decl.parent && arkts.isVariableDeclarator(decl.parent)) { + return isMemoVariableDeclarator(decl.parent); + } + return false; +} + +export function isMemoDeclaredClassProperty(decl: arkts.ClassProperty): boolean { + return isMemoClassProperty(decl); +} + +export function findMemoFromTypeAnnotation(typeAnnotation: arkts.AstNode | undefined): boolean { if (!typeAnnotation) { return false; } - if (arkts.isETSFunctionType(typeAnnotation)) { + if (arkts.isETSTypeReference(typeAnnotation) && !!typeAnnotation.part && !!typeAnnotation.part.name) { + let decl: arkts.AstNode | undefined = arkts.getDecl(typeAnnotation.part.name); + if (!decl || !arkts.isTSTypeAliasDeclaration(decl)) { + return false; + } + let isMemo: boolean = hasMemoAnnotation(decl) || hasMemoIntrinsicAnnotation(decl); + if (!isMemo && !!decl.typeAnnotation) { + isMemo = findMemoFromTypeAnnotation(decl.typeAnnotation); + } + return isMemo; + } else if (arkts.isETSFunctionType(typeAnnotation)) { return hasMemoAnnotation(typeAnnotation) || hasMemoIntrinsicAnnotation(typeAnnotation); } else if (arkts.isETSUnionType(typeAnnotation)) { return typeAnnotation.types.some( @@ -272,3 +480,179 @@ export function findMemoFromTypeAnnotation(typeAnnotation: arkts.TypeNode | unde } return false; } + +export function findReturnTypeFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): arkts.TypeNode | undefined { + if (!typeAnnotation) { + return undefined; + } + if (arkts.isETSTypeReference(typeAnnotation) && !!typeAnnotation.part && !!typeAnnotation.part.name) { + let decl: arkts.AstNode | undefined = arkts.getDecl(typeAnnotation.part.name); + if (!!decl && arkts.isTSTypeAliasDeclaration(decl)) { + return findReturnTypeFromTypeAnnotation(decl.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 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)) { + 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 mayAddLastReturn(node: arkts.BlockStatement): boolean { + 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 { + let gensymParamsCount = 0; + for (let i = 0; i < params.length; i++) { + if (params[i].ident.name.startsWith(RuntimeNames.GENSYM)) { + if (gensymParamsCount >= body.statements.length) { + throw new Error(`Expected ${params[i].ident.name} replacement to original parameter`); + } + const declaration = body.statements[gensymParamsCount]; + if (!arkts.isVariableDeclaration(declaration)) { + throw new Error(`Expected ${params[i].ident.name} replacement to original parameter`); + } + if (!arkts.isIdentifier(declaration.declarators[0].name)) { + throw new Error(`Expected ${params[i].ident.name} replacement to original parameter`); + } + params[i].ident = declaration.declarators[0].name; + gensymParamsCount++; + } + } + return gensymParamsCount; +} + +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 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, + isStableThis?: boolean +): ReturnTypeInfo { + const newReturnType = !!returnType + ? returnType.clone() + : arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + return { + node: newReturnType, + isMemo, + isVoid: isVoidType(newReturnType), + isStableThis, + }; +} + +export function buildeParamInfos(parameters: readonly arkts.ETSParameterExpression[]): ParamInfo[] { + return [ + ...parameters + .filter((it) => !hasMemoSkipAnnotation(it)) + .map((it) => { + return { ident: it.identifier, param: it }; + }), + ]; +} + +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; +} + +export function filterMemoSkipParams(params: readonly arkts.Expression[]): readonly arkts.Expression[] { + return params.filter((p) => !hasMemoSkipAnnotation(p as arkts.ETSParameterExpression)); +} diff --git a/arkui-plugins/package-lock.json b/arkui-plugins/package-lock.json deleted file mode 100644 index ec5de14b403fddba91cc38113de106c25536c6ac..0000000000000000000000000000000000000000 --- a/arkui-plugins/package-lock.json +++ /dev/null @@ -1,5871 +0,0 @@ -{ - "name": "arkui-plugin", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "arkui-plugin", - "version": "1.0.0", - "workspaces": [ - "./test" - ], - "dependencies": { - "@koalaui/libarkts": "../koala-wrapper" - }, - "devDependencies": { - "@babel/cli": "7.20.7", - "@babel/core": "7.20.12", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/preset-env": "7.20.2", - "@babel/preset-typescript": "7.18.6", - "@babel/runtime": "7.20.13", - "@types/jest": "^29.5.14", - "@types/node": "^22.13.9", - "jest": "^29.7.0", - "ts-jest": "^29.2.0", - "ts-node": "^10.9.0", - "typescript": "^5.0.0" - } - }, - "../koala-wrapper": { - "name": "@koalaui/libarkts", - "version": "1.0.0", - "devDependencies": { - "@babel/cli": "7.20.7", - "@babel/core": "7.20.12", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/preset-env": "7.20.2", - "@babel/preset-typescript": "7.18.6", - "@babel/runtime": "7.20.13", - "@tsconfig/recommended": "1.0.8", - "@types/node": "^18.0.0", - "node-addon-api": "^8.3.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@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, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.8", - "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" - }, - "bin": { - "babel": "bin/babel.js", - "babel-external-helpers": "bin/babel-external-helpers.js" - }, - "engines": { - "node": ">=6.9.0" - }, - "optionalDependencies": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", - "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", - "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", - "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", - "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", - "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@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, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@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, - "license": "ISC", - "dependencies": { - "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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/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, - "license": "MIT" - }, - "node_modules/@jest/transform/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@koalaui/libarkts": { - "resolved": "../koala-wrapper", - "link": true - }, - "node_modules/@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, - "license": "MIT", - "optional": true - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@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, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "22.15.31", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-22.15.31.tgz", - "integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arkui-plugins-test": { - "resolved": "test", - "link": true - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/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, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/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, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/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, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/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, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/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, - "license": "ISC", - "optional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/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==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/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, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/@babel/core": { - "version": "7.27.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.27.4.tgz", - "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/@babel/core/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/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, - "license": "MIT" - }, - "node_modules/istanbul-lib-instrument/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/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, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://repo.huaweicloud.com/repository/npm/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/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, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/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, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/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, - "license": "BSD-3-Clause" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "BSD-3-Clause" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "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" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/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, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@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" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/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, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/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, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "test": { - "name": "arkui-plugins-test", - "version": "1.0.0" - } - } -} diff --git a/arkui-plugins/package.json b/arkui-plugins/package.json index 20078aeb058c049004d2f7f7684b4601e9f10b56..929c12377e1d5259d15b24c986a6e41ff93b5e70 100644 --- a/arkui-plugins/package.json +++ b/arkui-plugins/package.json @@ -10,7 +10,10 @@ "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", - "test": "jest --config ./jest-test.config.ts", + "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 --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/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/state_link_interop.ets b/arkui-plugins/test/demo/interop/state_link_interop.ets new file mode 100644 index 0000000000000000000000000000000000000000..ec3778ef278c48762a799525d4deaa9430717344 --- /dev/null +++ b/arkui-plugins/test/demo/interop/state_link_interop.ets @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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{ + @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/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 aa3de2c82b56509d3f51e3ec7069f5c85ddea167..0d92ceecc8bba3706b7f2dcb6416e8452bde1476 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -1,24 +1,45 @@ { - "plugins": { - "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 38% 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 55175923963fabd78bd46b24eeb7679e4251a3a4..a665975076ca05f9e8111b218136fdbff21f123c 100755 --- a/arkui-plugins/test/demo/localtest/entry/new.ets +++ b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets @@ -13,57 +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, DecoratedMutableVariable } 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' +@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 = ""; // TODO: remove this + @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/animation/animation-basic.ets b/arkui-plugins/test/demo/mock/animation/animation-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..35c26e459a4c6afc5645652a62c0f063639a1703 --- /dev/null +++ b/arkui-plugins/test/demo/mock/animation/animation-basic.ets @@ -0,0 +1,17 @@ +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/custom-component/custom-component-call.ets b/arkui-plugins/test/demo/mock/builder-lambda/custom-component/custom-component-call.ets new file mode 100644 index 0000000000000000000000000000000000000000..4da86661b7a4e9e51841d53188ccc2b2d09a7c70 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/custom-component/custom-component-call.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 { Text, Column, Component, Builder, BuilderParam } from "@ohos.arkui.component" + +@Component +struct CustomContainer { + @Builder closerBuilder(){}; + @BuilderParam closer: () => void = this.closerBuilder; + + build() {} +} + +@Component +struct CustomContainerUser { + + build() { + Column() { + CustomContainer() { + Column() { + Text('hello') + } + } + CustomContainer({}) { + Column() {} + } + CustomContainer(undefined) {} + CustomContainer() + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/builder-lambda/instantiate.ets b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets similarity index 76% rename from arkui-plugins/test/demo/mock/builder-lambda/instantiate.ets rename to arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets index 1b05c1e8a00d24fabdf07d7456c8d55ab02c4a76..79886cd636abb115db86135911840d922c0f2e79 100644 --- a/arkui-plugins/test/demo/mock/builder-lambda/instantiate.ets +++ b/arkui-plugins/test/demo/mock/builder-lambda/simple-component.ets @@ -13,12 +13,11 @@ * limitations under the License. */ -import { Component } from "@koalaui.arkts-arkui.Common" -import { Column } from "@koalaui.arkts-arkui.Column" +import { memo } from "@ohos.arkui.stateManagement" +import { Column, ColumnAttribute } from "arkui.component.column" -@Component -struct MyStateSample { - build() { - Column() {} +class MyStateSample { + @memo build() { + Column(undefined) {} } } \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..a9b81be6801d733b9d0029d84f4298ce16ed41e8 --- /dev/null +++ b/arkui-plugins/test/demo/mock/builder-lambda/style-with-receiver.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 { memo } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { Text, TextAttribute, Column, Component} from "@ohos.arkui.component" +import hilog from '@ohos.hilog' + +@memo function cardStyle(this: TextAttribute, num: number, str: string): this { + this.fontSize(num); + this.backgroundColor(num); + return this; +} + +@memo function style22(this: TextAttribute): this { + this.fontWeight(700); + return this; +} + +@Component +struct MM { + build() { + Column() { + Text('hello world') + .height(200) + .fontColor('#000000') + .cardStyle(600, '#eeeeee') + .fontSize(60) + .fontWeight(400) + .style22() + .width(900) + Text('hello world') + .cardStyle(600, '#eeeeee') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/jest-test.config.ts b/arkui-plugins/test/demo/mock/common-utils/annotation.ets similarity index 62% rename from arkui-plugins/jest-test.config.ts rename to arkui-plugins/test/demo/mock/common-utils/annotation.ets index 8295964bc1ae1a4778008050e2f6e01bba6b7693..e8a78a587e9d3eb84e4e8de3d68bdef4238183c5 100644 --- a/arkui-plugins/jest-test.config.ts +++ b/arkui-plugins/test/demo/mock/common-utils/annotation.ets @@ -13,14 +13,20 @@ * limitations under the License. */ -export default { - testEnvironment: 'node', - transform: { - '^.+\\.ts$': ['ts-jest', { isolatedModules: true }], - }, - testRegex: './test/ut/.+\\.test\\.ts$', - moduleFileExtensions: ['ts', 'js', 'json', 'node'], - coverageDirectory: './test/report', - collectCoverageFrom: ['common/**', 'memo-plugins/**', 'ui-plugins/**'], - verbose: true, -}; +@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..acb51f102a13823e022524bfa36daaf55e4ddcf8 --- /dev/null +++ b/arkui-plugins/test/demo/mock/component/for-each.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 { 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) + }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/builder-param/builder-param-passing.ets b/arkui-plugins/test/demo/mock/decorators/builder-param/builder-param-passing.ets new file mode 100644 index 0000000000000000000000000000000000000000..c679899b2e1009491fcf877e6b9bde97c0ca433b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/builder-param/builder-param-passing.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, Entry, Builder, BuilderParam, Column, Text } from "@ohos.arkui.component" + +@Component +struct Child { + @Builder customBuilder() {}; + @BuilderParam customBuilderParam: () => void = this.customBuilder; + + build() { + this.customBuilderParam() + } +} + +@Component +struct Parent { + @Builder componentBuilder() { + Text('Parent builder') + } + + build() { + Column() { + Child({ customBuilderParam: this.componentBuilder }) + Child({ customBuilderParam: () => { this.componentBuilder() } }) + Child() { Text('Parent builder') } + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/builder-param/init-with-local-builder.ets b/arkui-plugins/test/demo/mock/decorators/builder-param/init-with-local-builder.ets new file mode 100644 index 0000000000000000000000000000000000000000..e1e2b46c94c01d62624e376aa3acd3fb2f740677 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/builder-param/init-with-local-builder.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, Builder, BuilderParam } from "@ohos.arkui.component" + +@Component +struct Child { + @Builder doNothingBuilder() {}; + @Builder doNothingBuilder2(str: string) {}; + @BuilderParam customBuilderParam: () => void = this.doNothingBuilder; + @BuilderParam customBuilderParam2: (str: string) => void = this.doNothingBuilder2; + + build(){ + this.customBuilderParam() + this.customBuilderParam2('hello') + } +} \ 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/local/@ohos.arkui.component.units.d.ets b/arkui-plugins/test/demo/mock/decorators/builder/global-builder.ets similarity index 58% rename from arkui-plugins/test/local/@ohos.arkui.component.units.d.ets rename to arkui-plugins/test/demo/mock/decorators/builder/global-builder.ets index c6a98e6281f1f588c75fa2dabba57a1d9c904c7e..bba1e6ee0cd7da5ac40d1cefae611358c19e9138 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.units.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/builder/global-builder.ets @@ -13,17 +13,29 @@ * limitations under the License. */ -import { Resource } from "@ohos.arkui.external.resource" -import { Color } from "@ohos.arkui.component.enums"; +import { Component, Row, Builder, Text } 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 showTextBuilder() { + Text('Hello World') +} -export type Dimension = PX | VP | FP | LPX | Percentage | Resource; +class Tmp { + paramA1: string = ''; +} -export type Length = string | number | Resource; +@Builder function overBuilder(params: Tmp) { + Row() { + Text('UseStateVarByReference: ' + params.paramA1) + } +} -export type ResourceColor = Color | number | string | Resource; \ No newline at end of file +@Component +struct BuilderDemo { + build() { + Row() { + showTextBuilder() + overBuilder({ paramA1: 'Hello' }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/builder/local-builder.ets b/arkui-plugins/test/demo/mock/decorators/builder/local-builder.ets new file mode 100644 index 0000000000000000000000000000000000000000..01da4671a674ee9df085abb3b4a1e9380d3a0936 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/builder/local-builder.ets @@ -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 { Component, Column, Builder, Text } from "@ohos.arkui.component" + +@Component +struct BuilderDemo { + @Builder + showTextBuilder() { + Text('Hello World') + .fontSize(30) + } + @Builder + showTextValueBuilder(param: string) { + Text(param) + .fontSize(30) + } + build() { + Column() { + this.showTextBuilder() + this.showTextValueBuilder('Hello @Builder') + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/link/link-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/link/link-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..9c97005bc712f64f8cd00fec145de8fb436f2b4b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/link/link-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 { Link } from "@ohos.arkui.stateManagement" + +@Component +struct LinkParent { + @Link linkVar1: string; + @Link linkVar2: number; + @Link linkVar3: boolean; + @Link linkVar4: undefined; + @Link linkVar5: null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/link/link-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/link/link-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..512968ab318240e491f9a6b673300786149ddeda --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/link/link-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 { Link } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum LinkType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @Link linkVar1: Per; + @Link linkVar2: Array; + @Link linkVar3: LinkType; + @Link linkVar4: Set; + @Link linkVar5: boolean[]; + @Link linkVar6: Array; + @Link linkVar7: Per[]; + @Link linkVar8: (sr: string)=>void; + @Link linkVar9: Date; + @Link linkVar10: Map; + @Link linkVar11: string | number; + @Link linkVar12: Set | Per; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets b/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets new file mode 100644 index 0000000000000000000000000000000000000000..b04d84e93c8f0f6e83a0be4e0afb54c5704d60aa --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.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, Column, TextInput } from "@ohos.arkui.component" +import { Link, State, Prop } from "@ohos.arkui.stateManagement" + +@Component +struct Parant { + @Link text1: string; + + build() { + Column() { + TextInput({ text: this.text1 }) + Child({ childText: this.text1, childText2: this.text1, childText3: this.text1, childText4: this.text1 }) + } + } +} + +@Component +struct Child { + @Link childText: string; + @State childText2: string = 'sss'; + @Prop childText3: string; + @Prop childText4: string = 'cc'; + + build() { + TextInput({ text: this.childText }) + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/link/state-to-link.ets b/arkui-plugins/test/demo/mock/decorators/link/state-to-link.ets new file mode 100644 index 0000000000000000000000000000000000000000..a3e02b8b7ee2f45b3a6654a6a8cfdfd99d3414c7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/link/state-to-link.ets @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, DatePicker, ClickEvent } from "@ohos.arkui.component" +import { Link, State } from "@ohos.arkui.stateManagement" + +@Component +struct DateComponent { + @Link selectedDate: Date; + + build() { + Column() { + Button('child increase the year by 1') + .onClick((e: ClickEvent) => { + this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1); + }) + Button('child update the new date') + .margin(10) + .onClick((e: ClickEvent) => { + this.selectedDate = new Date('2023-09-09'); + }) + DatePicker({ + start: new Date('1970-1-1'), + end: new Date('2100-1-1'), + selected: this.selectedDate + }) + } + + } +} + +@Entry +@Component +struct ParentComponent { + @State parentSelectedDate: Date = new Date('2021-08-08'); + + build() { + Column() { + Button('parent increase the month by 1') + .margin(10) + .onClick((e: ClickEvent) => { + this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1); + }) + Button('parent update the new date') + .margin(10) + .onClick((e: ClickEvent) => { + this.parentSelectedDate = new Date('2023-07-07'); + }) + DatePicker({ + start: new Date('1970-1-1'), + end: new Date('2100-1-1'), + selected: this.parentSelectedDate + }) + + DateComponent({ selectedDate:this.parentSelectedDate }) + } + } +} \ 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/objectlink/objectlink-basic.ets b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..6c06a5554efece86b1f7acfcd400376fbd48bb3f --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-basic.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" +import { Observed, ObjectLink } from "@ohos.arkui.stateManagement" + +@Observed +class A {} + +@Component +struct MyStateSample { + + @ObjectLink objectlinkvar: A; + + build() {} +} \ 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/objectlink/objectlink-observed.ets similarity index 33% rename from arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets rename to arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets index 728c949e75b926d8782cb3bc3fbb3b05ca67d079..73cc53cc0b59037dac3b14f09d8eb605e066520a 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.customComponent.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/objectlink/objectlink-observed.ets @@ -13,45 +13,57 @@ * 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 { Component, Entry, Column, Button, ClickEvent } from "@ohos.arkui.component" +import { State, ObjectLink, Observed } from "@ohos.arkui.stateManagement" + +@Observed +class DateClass extends Date { + constructor(args: number | string) { + super(args); + } +} + +@Observed +class NewDate { + public data: DateClass = new DateClass(11); + + constructor(data: DateClass) { + this.data = data; + } +} + +@Component +struct Child { + label: string = 'date'; + @ObjectLink data: DateClass; + + build() { + Column() { + Button(`child increase the day by 1`) + .onClick((e: ClickEvent) => { + this.data.setDate(this.data.getDate() + 1); + }) + } + } +} + +@Entry +@Component +struct Parent { + @State newData: NewDate = new NewDate(new DateClass('2023-1-1')); + + build() { + Column() { + Child({ label: 'date', data: this.newData.data }) + + Button(`parent update the new date`) + .onClick((e: ClickEvent) => { + this.newData.data = new DateClass('2023-07-07'); + }) + Button(`ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))`) + .onClick((e: ClickEvent) => { + this.newData = new NewDate(new DateClass('2023-08-20')); + }) + } + } } \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..b646f91c4207d279a79d501ba4c9dddc04c50c72 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-only.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" +import { Observed } from "@ohos.arkui.stateManagement" + +@Observed +class A { + propA: number = 1 + trackA: number = 2 +} + +@Component +struct MyStateSample { + build() {} +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..10a9ecd577221c482ab69a668951ffef56883133 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-class-property.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 { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +class Info{} +class E { + propE: Info = new Info() + @Track trackE: Info = new Info() +} + +@Observed +class E1 { + propE1: Info = new Info() + trackE1: Info = new Info() +} + +@Component +struct MyStateSample { + build() {} +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..a47a08b816fe9c2c221f05c75965a8ba32c1e00b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-complex-type.ets @@ -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 { Component, Entry } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +class Person{ +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Observed +class mixed1 { + @Track numA: number = 33; + @Track stringA: string = 'AA'; + @Track booleanA: boolean = true; + @Track arrayA: number[] = [1,2,3]; + @Track objectA: Object = {}; + @Track dateA: Date = new Date('2021-08-08'); + @Track setA: Set = new Set(); + @Track mapA: Map = new Map(); + @Track unionA: string | undefined = ""; + @Track classA: Person = new Person(); + @Track 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; +} + +@Observed +class mixed2 { + numA: number = 33; + stringA: string = 'AA'; + booleanA: boolean = true; + arrayA: number[] = [1,2,3]; + objectA: Object = {}; + dateA: Date = new Date('2021-08-08'); + setA: Set = new Set(); + mapA: Map = new Map(); + unionA: string | undefined = ""; + classA: Person = new Person(); + enumA: Status = Status.NotFound; +} + +class mixed3 { + @Track numA: number = 33; + @Track stringA: string = 'AA'; + @Track booleanA: boolean = true; + @Track arrayA: number[] = [1,2,3]; + @Track objectA: Object = {}; + @Track dateA: Date = new Date('2021-08-08'); + @Track setA: Set = new Set(); + @Track mapA: Map = new Map(); + @Track unionA: string | undefined = ""; + @Track classA: Person = new Person(); + @Track enumA: Status = Status.NotFound; +} + + +@Entry +@Component +struct MyStateSample { + build() { + + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..a9d102691bbf11ef993e7fb9682bdebc3adb3221 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-extends.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 { Component } from "@ohos.arkui.component" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Observed +class A { + propA: number = 1 + trackA: number = 2 +} + + +class G extends A { + propG: number = 1; +} + +@Observed +class H extends G { + @Track propG: number = 1; +} + +@Component +struct MyStateSample { + build() {} +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..8342cd8c277e619aa2582af34fe7d9195b53d859 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track-implements.ets @@ -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 { Component } from "@ohos.arkui.component" +import { Observed } from "@ohos.arkui.stateManagement" + interface PropInterface { + propF: number +} + +interface trackInterface { + trackF: number +} + +@Observed +class F implements PropInterface, trackInterface { + propF: number = 1 + trackF: number = 2 +} + +@Component +struct MyStateSample { + build() {} +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..98d037faab81d8617518243c6a0718daab34b54d --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/observed-track.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" +import { Observed, Track } from "@ohos.arkui.stateManagement" + +@Observed +class B { + propB: number = 1 + @Track trackB: number = 2 +} + +@Component +struct MyStateSample { + build() {} +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..08239c86f16a9ca62d911f6188714cb23cdf4e24 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/observed-track/track-only.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 } from "@ohos.arkui.component" +import { Track } from "@ohos.arkui.stateManagement" + +class C { + propC: number = 1 + @Track trackC: number = 2 +} + +@Component +struct MyStateSample { + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop/prop-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/prop/prop-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..bcfff2d589ac24d532da6c04ddc2ca008acf5cf0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/prop-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 { Prop } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @Prop propVar1: string = 'propVar1'; + @Prop propVar2: number = 50; + @Prop propVar3: boolean = true; + @Prop propVar4: undefined = undefined; + @Prop propVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop/prop-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/prop/prop-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..a2fbdc9d2387657602f4edd681f64b82a737f645 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/prop-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 { Prop } 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 { + @Prop propVar1: Per = new Per(6); + @Prop propVar2: Array = new Array(3,6,8); + @Prop propVar3: PropType = PropType.TYPE3; + @Prop propVar4: Set = new Set(new Array('aa', 'bb')); + @Prop propVar5: boolean[] = [true, false]; + @Prop propVar6: Array = new Array(new Per(7), new Per(11)); + @Prop propVar7: Per[] = [new Per(7), new Per(11)]; + @Prop propVar8: (sr: string)=>void = (sr: string)=>{}; + @Prop propVar9: Date = new Date('2025-4-23'); + @Prop propVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Prop propVar11: string | number = 0.0; + @Prop propVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.ets b/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.ets new file mode 100644 index 0000000000000000000000000000000000000000..38af709df0adab049df6551c806c570acaa01019 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.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 { Prop, State } from "@ohos.arkui.stateManagement" + +@Component +struct CountDownComponent { + @Prop 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/provide-annotation-usage.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-annotation-usage.ets new file mode 100644 index 0000000000000000000000000000000000000000..74afaf8f9bad2eeac1424a9ceaa238fd9b3e6a3b --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-annotation-usage.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 { Component } from "@ohos.arkui.component" +import { Provide } from "@ohos.arkui.stateManagement" + +@Component +struct Ancestors { + @Provide count: string | undefined = 'Child0'; + @Provide({ alias: 'prov1' }) count1: string | undefined = 'Child1'; + @Provide({ alias: 'prov2', allowOverride: false }) count2: string | undefined = 'Child2'; + @Provide({ alias: 'prov3', allowOverride: true }) count3: string | undefined = 'Child3'; + @Provide({ allowOverride: false }) count4: string | undefined = 'Child4'; + @Provide({ allowOverride: true }) count5: string | undefined = 'Child5'; + @Provide({ alias: "", allowOverride: true }) count6: string | undefined = 'Child6'; + @Provide({ alias: "" }) count7: string | undefined = 'Child7'; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..06c9ddea34a229226d70a0386a8d27f84b66d881 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-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 { Provide } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @Provide provideVar1: string = 'propVar1'; + @Provide provideVar2: number = 50; + @Provide provideVar3: boolean = true; + @Provide provideVar4: undefined = undefined; + @Provide provideVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..6395a981b8ff07f5626146189ae534a0840efeb9 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/provide-and-consume/provide-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 { Provide } 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 { + @Provide provideVar1: Per = new Per(6); + @Provide provideVar2: Array = new Array(3,6,8); + @Provide provideVar3: PropType = PropType.TYPE3; + @Provide provideVar4: Set = new Set(new Array('aa', 'bb')); + @Provide provideVar5: boolean[] = [true, false]; + @Provide provideVar6: Array = new Array(new Per(7), new Per(11)); + @Provide provideVar7: Per[] = [new Per(7), new Per(11)]; + @Provide provideVar8: (sr: string)=>void = (sr: string)=>{}; + @Provide provideVar9: Date = new Date('2025-4-23'); + @Provide provideVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Provide provideVar11: string | number = 0.0; + @Provide provideVar12: Set | Per = new Per(6); + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f77f9a04b97aaf59e97607b85e067610159eed97 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-build.ets @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, $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.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.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.page_text_font_size'), + bottom: $r('app.float.page_text_font_size') + } as Margin) + ImageAnimator().images([ + { + src: $r('app.media.app_icon') + }, + { + src: $r('app.media.layered_image') + }, + ]) + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..fca6a78a7096aef65412f00b4d3ebb233a7cc517 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/resource/resource-in-property.ets @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, $r, $rawfile, Column, Text, Image, Resource } from "@ohos.arkui.component" + +let i: Resource = $r('app.string.app_name'); + +@Component +struct ResourceComponent { + private str: Resource = $r('app.string.app_name'); + private icon: Resource = $rawfile('app.mock.txt'); + build() { + Column() { + Text(this.str) + Text(i) + Image(this.icon) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..547ed62ea0ace3b2aac355c254ae89cf206fd4cf --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.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 { Component, Reusable} from "@ohos.arkui.component" +import { State, Prop } from "@ohos.arkui.stateManagement" + +@Component +struct MyStateSample { + build() { + Child({ num: 5 } ) + } +} + +@Component +@Reusable +struct Child { + @Prop num: number = 1 + @State num1: number = 2 + build() { + + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets new file mode 100644 index 0000000000000000000000000000000000000000..10bf9f803439f53758ecae5b653297554f1150d3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-complex.ets @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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" + +class Message { + value: string | undefined; + + constructor(value: string) { + this.value = value; + } +} + +@Entry +@Component +struct Index { + @State display: boolean = true; + + build() { + Column() { + Button('Hello') + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick((e: ClickEvent) => { + this.display = !this.display; + }) + if (this.display) { + // 如果只有一个复用的组件,可以不用设置reuseId + Child({ message: new Message('Child') }) + } + } + .height("100%") + .width('100%') + } +} + +@Reusable +@Component +struct Child { + @State message: Message = new Message('AboutToReuse'); + + aboutToReuse(params: Record) { + console.info("Recycle ====Child=="); + //this.message = params.message as Message; + } + + build() { + Column() { + Text(this.message.value) + .fontSize(30) + } + .borderWidth(1) + .height(100) + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/state/state-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/state/state-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..b7e039a774cfb91d4069786c69be741ced0e1387 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/state/state-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 { State} from "@ohos.arkui.stateManagement" + +@Component +struct Parent { + @State stateVar1: string = 'stateVar1'; + @State stateVar2: number = 50; + @State stateVar3: boolean = true; + @State stateVar4: undefined = undefined; + @State stateVar5: 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/state/state-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/state/state-complex-type.ets index 2ff6ea60ae0b042792ac8796396371e815a65a0e..5ffe6f50afc67bdfa63f3434e3a336541e19df20 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.common.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/state/state-complex-type.ets @@ -13,43 +13,37 @@ * 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 { Component } from "@ohos.arkui.component" +import { State} 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; +@Component +struct Parent { + @State stateVar1: Per = new Per(6); + @State stateVar2: Array = new Array(3,6,8); + @State stateVar3: StateType = StateType.TYPE3; + @State stateVar4: Set = new Set(new Array('aa', 'bb')); + @State stateVar5: boolean[] = [true, false]; + @State stateVar6: Array = new Array(new Per(7), new Per(11)); + @State stateVar7: Per[] = [new Per(7), new Per(11)]; + @State stateVar8: (sr: string)=>void = (sr: string)=>{}; + @State stateVar9: Date = new Date('2025-4-23'); + @State stateVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @State stateVar11: string | number = 0.0; + @State stateVar12: Set | Per = new Per(6); + + build() { + } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/state/state-to-state.ets b/arkui-plugins/test/demo/mock/decorators/state/state-to-state.ets new file mode 100644 index 0000000000000000000000000000000000000000..8beb2e452478cc3acb574efd33c5ed242d295354 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/state/state-to-state.ets @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { State } from "@ohos.arkui.stateManagement" + +class Per { + str: string; + constructor(str: string) { + this.str = str; + } +} + +@Component +struct Parent { + @State parentVar1: Per = new Per('hello'); + build() { + Column() { + Child({ childVar1: this.parentVar1 }) + } + } +} + +@Component +struct Child { + @State childVar1: Per = new Per('ccc'); + build() { + Text(this.childVar1.str) + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.ets new file mode 100644 index 0000000000000000000000000000000000000000..bfe60142ca9fe9a19103a48a8b00b648f0a89b85 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-appstorage.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, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" +import { StorageLink, AppStorage } from "@ohos.arkui.stateManagement" + +class Data { + code: number; + + constructor(code: number) { + this.code = code; + } +} + +AppStorage.setOrCreate('PropA', 47, Type.from()); +AppStorage.setOrCreate('PropB', new Data(50), Type.from()); + +@Entry +@Component +struct Index { + @StorageLink('PropA') storageLink: number = 1; + @StorageLink('PropB') storageLinkObject: Data = new Data(1); + + build() { + Column() { + Text(`From AppStorage ${this.storageLink}`) + .onClick((e: ClickEvent) => { + this.storageLink += 1; + }) + + Text(`From AppStorage ${this.storageLinkObject.code}`) + .onClick((e: ClickEvent) => { + this.storageLinkObject.code += 1; + }) + } + } +} diff --git a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets similarity index 45% rename from arkui-plugins/test/local/@ohos.arkui.component.column.d.ets rename to arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets index 915dc5cebb40010bb552e9afc7e12b6b74ff64b6..08b9113960050e21e0a8ec028dcf9983bec52514 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.column.d.ets +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-complex-type.ets @@ -12,30 +12,31 @@ * 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" -import { memo } from '../stateManagement/runtime'; -import { ComponentBuilder, CommonMethod, PointLightStyle } from './common'; -import { HorizontalAlign, FlexAlign } from './enums'; - -export declare interface ColumnOptions { - space?: string | number; +class Person{ + name: string = '' + constructor(name: string){} } -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; +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 } -@memo -@ComponentBuilder -export declare function Column ( - options?: ColumnOptions, - @memo - content?: () => void -): ColumnAttribute; \ No newline at end of file +@Entry +@Component +struct MyStateSample { + @StorageLink('Prop1') arrayA: number[] = [1,2,3]; + @StorageLink('Prop2') objectA: Object = {}; + @StorageLink('Prop3') dateA: Date = new Date('2021-08-08'); + @StorageLink('Prop4') setA: Set = new Set(); + @StorageLink('Prop5') mapA: Map = new Map(); + @StorageLink('Prop7') classA: Person = new Person("John"); + @StorageLink('Prop8') enumA: Status = Status.NotFound; + + build() {} +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..a4219a46368068ad3c4c32197463fb72eb7e31ac --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storagelink/storagelink-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 { StorageLink } from "@ohos.arkui.stateManagement" + +@Entry +@Component +struct MyStateSample { + @StorageLink('Prop1') numA: number = 33; + @StorageLink('Prop2') stringA: string = 'AA'; + @StorageLink('Prop3') booleanA: boolean = true; + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..73457d2a14fc27ed256284992b523ca868c616a7 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.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, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" +import { StorageProp, AppStorage } from "@ohos.arkui.stateManagement" + +class Data { + code: number; + + constructor(code: number) { + this.code = code; + } +} + +AppStorage.setOrCreate('PropA', 47, Type.from()); +AppStorage.setOrCreate('PropB', new Data(50), Type.from()); + +@Entry +@Component +struct Index { + @StorageProp('PropA') storageProp: number = 1; + @StorageProp('PropB') storagePropObject: Data = new Data(1); + + build() { + Column() { + Text(`From AppStorage ${this.storageProp}`) + .onClick((e: ClickEvent) => { + this.storageProp += 1; + }) + + Text(`From AppStorage ${this.storagePropObject.code}`) + .onClick((e: ClickEvent) => { + this.storagePropObject.code += 1; + }) + } + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..50c2ea128a534ea463a12c780b1fa27ab051fe10 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Entry +@Component +struct MyStateSample { + @StorageProp('Prop1') arrayB: number[] = [1,2,3]; + @StorageProp('Prop2') objectB: Object = {}; + @StorageProp('Prop3') dateB: Date = new Date('2021-09-09'); + @StorageProp('Prop4') setB: Set = new Set(); + @StorageProp('Prop5') mapB: Map = new Map(); + @StorageProp('Prop7') classB: Person = new Person("Kevin"); + @StorageProp('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..95d4ed58d42aaa7f76fbdfe8b1c6798ac2e22b6f --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-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" // TextAttribute should be insert by ui-plugins +import { StorageProp } from "@ohos.arkui.stateManagement" + +@Entry +@Component +struct MyStateSample { + @StorageProp('Prop1') numB: number = 43; + @StorageProp('Prop2') stringB: string = 'BB'; + @StorageProp('Prop3') booleanB: boolean = false; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..4c36212e7da874ab1268d3e671f7f6d69c867ee3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets @@ -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 { Component, Entry, Column } from "@ohos.arkui.component" +import { State, Prop, StorageLink, StorageProp, Link, Watch, ObjectLink, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" + +@Observed +class A { + propA: string = 'hello' + @Track trackA: string = 'world' +} + +@Entry +@Component +struct MyStateSample { + @State @Watch('stateOnChange') statevar: string = 'Hello World'; + @Prop @Watch('propOnChange') propvar: string = 'Hello World'; + @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; + @Provide @Watch('ProvideOnChange') providevar: string = 'Hello World'; + + stateOnChange(propName: string) {} + propOnChange(propName: string) {} + linkOnChange(propName: string) {} + storageLinkOnChange(propName: string) {} + storagePropOnChange(propName: string) {} + objectLinkOnChange(propName: string) {} + ProvideOnChange(propName: string) {} + + build() { + Column() { + Child() + } + } +} + +@Component +struct Child { + @Consume @Watch('ConsumeOnChange') providevar: 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.stateManagement.d.ets b/arkui-plugins/test/demo/mock/entry/entry-only.ets similarity index 79% rename from arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets rename to arkui-plugins/test/demo/mock/entry/entry-only.ets index 9cb01ec593ed40cc9c98940e4d1b8dcc9b7bdd33..2d54e1bf2b273c0de5421035703a6d13acb48b82 100644 --- a/arkui-plugins/test/local/@ohos.arkui.stateManagement.d.ets +++ b/arkui-plugins/test/demo/mock/entry/entry-only.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() +@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/demo/mock/entry/route-name/route-name.ets b/arkui-plugins/test/demo/mock/entry/route-name/route-name.ets new file mode 100644 index 0000000000000000000000000000000000000000..3a9fedb0676064cc6663fddcc204e770021742d3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/entry/route-name/route-name.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, Entry } from "@ohos.arkui.component" + +@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/local/@ohos.arkui.component.styledString.d.ets b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets similarity index 41% rename from arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets rename to arkui-plugins/test/demo/mock/memo/functions/argument-call.ets index 4a0e94d6f089aab4442c94ca5d4f960aa70240af..1db0074a76a549f58dd45d7ee78a04cbf23e5c25 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.styledString.d.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/argument-call.ets @@ -13,20 +13,40 @@ * limitations under the License. */ -export declare class StyledString { - constructor(value: string /*| ImageAttachment | CustomSpan, styles?: Array*/); +import { memo } from "arkui.stateManagement.runtime"; - readonly length: number; +@memo function memo_arg_call( + arg1: number, + arg2: (x: number) => number, + @memo arg3: (x: number) => number, + arg4?: (x: number) => number, + @memo arg5?: (x: number) => number, +) { + arg2(arg1) + arg3(arg1) + arg4?.(arg1) + arg5?.(arg1) +} - getString(): string; - // getStyles(start: number, length: number, styledKey?: StyledStringKey): Array; - equals(other: StyledString): boolean; - subStyledString(start: number, length?: number): StyledString; +@memo +function memo_arg_call_with_lowering( + arg1: number, + arg4?: (x: number) => number, + @memo arg5?: (x: number) => number, +) { + {let gensym___1 = arg4; + ((gensym___1 == null) ? undefined : gensym___1(arg1))}; + {let gensym___2 = arg5; + ((gensym___2 == null) ? undefined : gensym___2(arg1))}; +} - 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; -} \ No newline at end of file +@memo +function args_with_default_values( + arg1: int = 10, + @memo arg2: () => int = () => { return 20 }, + arg3: int = arg1, + arg4?: int +): void { + console.log(arg1, arg2, arg3, arg4) + console.log(arg2()) +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..6ab9e8d74e1833a28762e1a5bd35227a30d1170c --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/functions/declare-and-call.ets @@ -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 { memo } from "arkui.stateManagement.runtime"; + +@memo +declare function funcA(): void; + +class A { + @memo foo() { + funcA(); + } +} + +@memo +function funcB(): void { + funcA(); +} + +@memo +() => { + funcA(); +} \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..40759ede45a377cb8fa44059707f46eb23b8cd6c --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/functions/inner-functions.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 { memo } from "arkui.stateManagement.runtime"; + +@memo +function foo() { } + +@memo +function bar() { + const qux = @memo () => { + foo(); + } + + const other = () => {} +} + +class A { + @memo goo() { + let func = () => {}; + let func2 = @memo () => { + 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/local/@ohos.arkui.component.enums.d.ets b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets similarity index 51% rename from arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets rename to arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets index e72c21ace10322a6fb0fdef80711a7d34bb98173..fca54fcc759b39492c91e9636aa77b2399764ff6 100644 --- a/arkui-plugins/test/local/@ohos.arkui.component.enums.d.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/non-void-return-type.ets @@ -13,41 +13,52 @@ * limitations under the License. */ -export declare enum FlexAlign { - Start = 0, - Center = 1, - End = 2, - SpaceBetween = 3, - SpaceAround = 4, - SpaceEvenly = 5 -} - -export declare enum HorizontalAlign { - Start = 0, - Center = 1, - End = 2 -} - -export declare enum IlluminatedType { - NONE = 0, - BORDER = 1, - CONTENT = 2, - BORDER_CONTENT = 3, - BLOOM_BORDER = 4, - BLOOM_BORDER_CONTENT = 5 -} - -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 +import { memo } from "arkui.stateManagement.runtime"; + +@memo +function funcNum(): number { + return 1; +} + +@memo +function funcStr(): string { + return "1"; +} + +@memo +function funcBool(): boolean { + return false; +} + +interface A { + str: string; +} + +@memo +function funcA(): A { + return { str: "1" }; +} + +type B = (str: string) => void; + +@memo +function funcB(): B { + return (str: string) => {}; +} + +class C { + str: string; + constructor(str: string) { + this.str = str; + } +} + +@memo +function funcC(): C { + return new C("1"); +} + +@memo +function funcD(): () => void { + return () => {}; } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets b/arkui-plugins/test/demo/mock/memo/functions/type-reference.ets new file mode 100644 index 0000000000000000000000000000000000000000..fb4e922ace484c2f51b04bd95a69fa51135b73d3 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/functions/type-reference.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 { memo } from "arkui.stateManagement.runtime"; + +@memo type ItemBuilder = (item: Item) => void; + +interface Item { + item: T; +} + +interface Attribute { + @memo each(@memo itemGenerator: ItemBuilder): Attribute; +} + +@memo +export declare function A(): Attribute + +@memo +function func(): ItemBuilder { + return (item: Item): void => {}; +} + +class B { + @memo build() { + A().each((ri: Item) => {}) + } +} diff --git a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets similarity index 86% rename from arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets rename to arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets index 9cc3b09e07de69770a6f394122713c2a0853a371..5605e2b68528cfd0c7059d89672b9f3bce69da85 100644 --- a/arkui-plugins/test/local/@ohos.arkui.external.resource.d.ets +++ b/arkui-plugins/test/demo/mock/memo/functions/void-return-type.ets @@ -13,4 +13,9 @@ * limitations under the License. */ -export type Resource = boolean; \ No newline at end of file +import { memo } from "arkui.stateManagement.runtime"; + +@memo +function func(): void { + return; +} diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.ets new file mode 100644 index 0000000000000000000000000000000000000000..455fb68438738748a2eab6c3ee1362b6df4186a4 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/lambdas/argument-call.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 { memo } from "arkui.stateManagement.runtime"; + +(arg: (()=>void)) => {}(() => {}); + +(arg: @memo (()=>void)) => {}(@memo () => {}); + +(arg: @memo (()=>void) = () => {}) => {}(@memo () => {}); + +@memo +() => { + (@memo (arg: @memo (()=>void) = () => {}) => {})(@memo () => {}); +} + +@memo +() => { + let goo = @memo (name: string = "old") => {} + + goo(); +} + +() => { + let foo = (arg: @memo (()=>void) = () => {}) => {} + + foo(@memo () => {}); +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..4ade068c9d96371c0f1c69234d842196096d851a --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/lambdas/function-with-receiver.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 { memo } from "arkui.stateManagement.runtime"; + +class B { + @memo internal_call(): B { + this.foo1('morning'); + return this.foo2('afternoon') + } +} + +@memo function foo1(this: B, str: string): void { + console.log('Good', str); +} + +@memo function foo2(this: B, str: string): this { + console.log('Good', str); + return this; +} diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.ets new file mode 100644 index 0000000000000000000000000000000000000000..1bfdb18b91d4b8d74eda0378c1d582722b904953 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/lambdas/trailing-lambdas.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 { memo } from "arkui.stateManagement.runtime"; + +class A { + @memo foo(p?: ()=>void): void {} + + goo(@memo p?: ()=>void): void {} + + @memo koo(@memo p?: ()=>void): void {} +} + +@memo +function bar(f?: ()=>void): void {} + +function par(f?: @memo ()=>void): void {} + +@memo +function kar(@memo f?: ()=>void): void {} + +@memo +() => { + let a = new A(); + a.foo() { console.log(); } + a.goo() { console.log(); } + a.koo() { console.log(); } + + bar() { console.log(); } + par() { console.log(); } + kar() { console.log(); } +} diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.ets new file mode 100644 index 0000000000000000000000000000000000000000..2f5afae38d6f4aa2b77c1575cdcf6a995b13d48e --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/lambdas/void-lambda.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 { memo } from "arkui.stateManagement.runtime"; + +@memo +(): void => { + return; +} + +@memo +(arg?: () => string): void => { + return; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets new file mode 100644 index 0000000000000000000000000000000000000000..556aba2b53c44be09e8401ec1b53a4fefffb168c --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.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 { 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 +() => { + let x = new Person(); + x.fullName(() => {}); + + let f1: F1 = foo; + let f2: F2 = goo; + + let a = new A(); + a.f1(() => {}); + f1(a, () => {}); + + f2(a, () => {}); +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets b/arkui-plugins/test/demo/mock/memo/methods/argument-call.ets new file mode 100644 index 0000000000000000000000000000000000000000..25903f0435824055e305a26e3cc35c4c0d06c08e --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/argument-call.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 { memo } from "arkui.stateManagement.runtime"; + +class Test { + @memo lambda_arg(@memo arg: () => void) { + + } + + @memo lambda_arg_with_arg(@memo arg: (value: string) => string) { + + } + + @memo memo_content(@memo content: () => void) { + content() + } + + @memo compute_test( + @memo arg1: (() => void) | undefined, + arg2: (() => void) | undefined, + content: (() => void) | undefined + ): void { + + } +} + +class Use { + @memo test() { + const test = new Test() + + test.lambda_arg((): void => {}) + test.lambda_arg_with_arg((value: string): string => value) + + test.compute_test(() => {}, () => {}, () => {}) + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/methods/callable.ets b/arkui-plugins/test/demo/mock/memo/methods/callable.ets new file mode 100644 index 0000000000000000000000000000000000000000..bebbb75019c01d90cec62c17232ca22e16d66002 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/callable.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 { memo } from "arkui.stateManagement.runtime"; + +class A { + @memo + static $_invoke(): void {} +} + +class B { + static $_invoke(@memo p?: () => void): void {} +} + +class C { + @memo + static $_instantiate(factory: () => C): C { + return factory(); + } +} + +class D { + static $_instantiate(factory: () => D, @memo content?: () => void): D { + return factory(); + } +} + +@memo +() => { + A(); + B(() => {}); + + let x: C | D = C(); + x = D(); +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..121e1568d790c188fed431cb9a8c618cf1d56f21 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/declare-and-call.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 { memo } from "arkui.stateManagement.runtime"; + +declare abstract class A { + @memo + x(): void; + + test_signature( + @memo arg1: () => void, + @memo arg2: (() => void) | undefined, + @memo arg3: ((() => void) | undefined) | ((() => int) | undefined), + @memo x: (y: (z: @memo () => void) => void) => void, + ): @memo () => void; +} + +class AA extends A { + @memo x(): void {} +} + +@memo +() => { + new AA().x(); + + const a: A = new AA(); + a.x(); +} + diff --git a/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets new file mode 100644 index 0000000000000000000000000000000000000000..5dcd4c2920d870d0f7c9c3f7505365558ba14261 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/internal-calls.ets @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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"; + +export declare function __context(): __memo_context_type +export declare function __id(): __memo_id_type +type MemoType = @memo () => void + +class Test { + @memo void_method(): void { + } + + @memo internal_call() { + this.void_method() + } + + @memo method_with_internals() { + __context() + __id() + } + + memo_lambda() { + @memo () => { + + } + } + + @memo memo_variables() { + @memo const f = (): number => { + return 123 + }, g = (x: number): number => { + return 123 + x + } + + const h = @memo (): number => { + return 1 + } + + f() + g(1) + h() + } + + @memo args_with_default_values( + arg1: int = 10, + arg2: () => int = () => { return 20 }, + arg3: int = arg1, + arg4?: int + ): void { + console.log(arg1, arg2, arg3, arg4) + console.log(arg2()) + } + + @memo optional_args( + arg1?: int, + arg2?: () => int + ) { + console.log(arg1) + console.log(arg2) + console.log(arg2?.()) + } + + @memo type_alias( + arg: MemoType + ) { + arg() + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000000000000000000000000000000000..4e8053d506e6620b8fa153035afadfb4c3f2374d --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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"; + +@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 + +class Test { + @memo void_method(): void { + } + + @memo string_method_with_return(arg: string): string { + return arg + } + + @memo method_with_type_parameter(arg: T): T { + return arg + } + + @memo_intrinsic intrinsic_method(): int { + return 0 + } + + @memo_intrinsic intrinsic_method_with_this(): int { + this.void_method() + return 0 + } + + @memo_entry memoEntry(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo entry: () => R): R { + const getContext = () => { + return __context() + } + const getId = () => { + return __id() + } + { + const __memo_context = getContext() + const __memo_id = getId() + 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 { + @memo test() { + const test = new Test() + + test.string_method_with_return("a string") + test.method_with_type_parameter("I'm string") + } +} diff --git a/arkui-plugins/test/demo/mock/memo/methods/void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets new file mode 100644 index 0000000000000000000000000000000000000000..d2ee07cd7e93b8b41fda05fe6b3fbb99b7c3ba58 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/methods/void-method.ets @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 } from "arkui.stateManagement.runtime"; + +class A { + x: int + y: int +} + +class Test { + @memo void_method(): void { + } + + @memo a_method_with_implicit_return_type() { + } + + @memo void_method_with_arg(arg: string) { + } + + @memo void_method_with_return(arg: string) { + return + } + + @memo static static_method_with_type_parameter(arg: T): void { + return + } + + @memo obj_arg(arg: A) { + + } +} + +class Use { + @memo test() { + const test = new Test() + + test.void_method() + test.void_method_with_arg("an arg") + test.void_method_with_return("a value") + Test.static_method_with_type_parameter("I'm static") + + test.obj_arg({ x: 1, y: 2 }) + } +} diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets new file mode 100644 index 0000000000000000000000000000000000000000..a349e0450c4956e6a0a220f7ab61c47a0ebc9866 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/properties/class-constructor.ets @@ -0,0 +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 { memo } from "arkui.stateManagement.runtime"; + +interface A { + @memo a: () => void +} + +class AA { + + @memo a: (() => void) | undefined + + constructor(arg?: A) { + this.a = arg?.a; + } + + @memo + build() { + this.a?.(); + } +} + +@memo +() => { + let a = new AA({ a: () => {} }) ; +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets new file mode 100644 index 0000000000000000000000000000000000000000..79c72508bb5bed5f84aec0d417feff5052e868af --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/properties/class-properties.ets @@ -0,0 +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 { memo } from "arkui.stateManagement.runtime"; + +class A { + arg: () => void + @memo memo_arg: () => void + @memo memo_optional_arg?: () => void + @memo memo_union_arg: (() => void) | undefined = () => {} + + arg_memo_type: @memo () => void + + constructor() { + this.arg = () => {}; + this.memo_arg = () => {}; + this.arg_memo_type = () => {}; + } + + @memo + build() { + this.arg(); + this.memo_arg(); + this.arg_memo_type(); + } + +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/properties/implements.ets b/arkui-plugins/test/demo/mock/memo/properties/implements.ets new file mode 100644 index 0000000000000000000000000000000000000000..cdf8c6644bbca8d22526707e9cf4d68604003e87 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/properties/implements.ets @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 } from "arkui.stateManagement.runtime"; + +interface A { + @memo prop: (() => void) | undefined +} + +class AA implements A { + @memo prop: (() => void) | undefined +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets b/arkui-plugins/test/demo/mock/memo/properties/interfaces.ets new file mode 100644 index 0000000000000000000000000000000000000000..80a9332fd4409f6f40c01232599b0aadb3dee521 --- /dev/null +++ b/arkui-plugins/test/demo/mock/memo/properties/interfaces.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 { memo } from "arkui.stateManagement.runtime"; + +interface A { + arg: () => void + @memo memo_arg: () => void + @memo memo_optional_arg?: () => void + @memo memo_union_arg: (() => void) | undefined + + arg_memo_type: @memo () => void +} + +@memo() (() => { + let a: A = { + arg: (() => {}), + memo_arg: (() => {}), + memo_union_arg: (() => {}), + arg_memo_type: (() => {}), + }; +}); \ No newline at end of file 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/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-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/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 51154bd397d04b3440985a1a2d18f2330b6a72b3..46947c865d37f49fcd57c1804898a9d9a0288558 100644 --- a/arkui-plugins/test/package.json +++ b/arkui-plugins/test/package.json @@ -1,12 +1,23 @@ { "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", "compile:gdb": "gdb --args node $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/driver/build-system/dist/entry.js ./demo/hello_world/build_config.json", - "compile:plugins": "npm run compile --prefix .. ", + "compile:plugins": "npm run compile --prefix ..", + "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 7091e7d52812c0cb023e3ea532cd6c621b318f8b..663454255f56cdc6f457a0ce86e45b7c01be1860 100644 --- a/arkui-plugins/test/ut/common/annotation.test.ts +++ b/arkui-plugins/test/ut/common/annotation.test.ts @@ -14,37 +14,211 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PluginTestContext, PluginTester } from '../../utils/plugin-tester'; +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 pluginTester = new PluginTester('test arkts-utils'); +const COMMON_UTILS_DIR_PATH: string = 'common-utils'; -function testAnnotation(this: PluginTestContext): void { - const anno: arkts.AnnotationUsage = annotation('State'); - expect(arkts.isAnnotationUsage(anno)).toBeTruthy(); - expect(anno.dumpSrc()).toBe('@State()'); +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); + +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 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', - }, - { - beforeEach: [ - () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }, - ], - afterEach: [ - () => { - jest.spyOn(console, 'warn').mockRestore(); - }, - ], } ); 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 new file mode 100644 index 0000000000000000000000000000000000000000..1506b9ca1a38bdce1d4197f24956746f45c044be --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'argument-call.ets')]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() 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) { + __memo_scope.cached; + return; + } + __memo_parameter_arg2.value(__memo_parameter_arg1.value); + __memo_parameter_arg3.value(__memo_context, ((__memo_id) + ()), __memo_parameter_arg1.value); + ({let gensym%%_ = __memo_parameter_arg4.value; + (((gensym%%_) == (null)) ? undefined : gensym%%_(__memo_parameter_arg1.value))}); + ({let gensym%%_ = __memo_parameter_arg5.value; + (((gensym%%_) == (null)) ? undefined : gensym%%_(__memo_context, ((__memo_id) + ()), __memo_parameter_arg1.value))}); + { + __memo_scope.recache(); + return; + } +} +@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) { + __memo_scope.cached; + return; + } + { + let gensym___ = __memo_parameter_arg4.value; + (((gensym___) == (null)) ? undefined : gensym___(__memo_parameter_arg1.value)); + } + { + let gensym___ = __memo_parameter_arg5.value; + (((gensym___) == (null)) ? undefined : gensym___(__memo_context, ((__memo_id) + ()), __memo_parameter_arg1.value)); + } + { + __memo_scope.recache(); + return; + } +} +@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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + return __memo_scope.recache(20); + }) as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int))); + let arg3: int = (((gensym%%_) !== (undefined)) ? gensym%%_ : (arg1 as int)); + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 4); + 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); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + console.log(__memo_parameter_arg1.value, __memo_parameter_arg2.value, __memo_parameter_arg3.value, __memo_parameter_arg4.value); + console.log(__memo_parameter_arg2.value(__memo_context, ((__memo_id) + ()))); + { + __memo_scope.recache(); + return; + } +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform argument calls in functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..a1eb3091e9ac4484ce1fe57536a6af26b0c1602f --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'declare-and-call.ets'), +]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() ((__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) + ())); + { + __memo_scope.recache(); + return; + } +}); +@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; + return; + } + funcA(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } +} +class A { + @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; + return; + } + funcA(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform declare functions and calls', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..a73b950b6e13f509ac97580facb00a16306e1f18 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts @@ -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. + */ + +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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'inner-functions.ets'), +]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() 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; + return; + } + { + __memo_scope.recache(); + return; + } +} +@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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + foo(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + }); + const other = (() => {}); + { + __memo_scope.recache(); + return; + } +} +class A { + @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; + } + let func = (() => {}); + 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; + return; + } + foo(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + }); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform inner functions', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..c59453f5f38a497aedc52bc68b4102fc6d85c33f --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts @@ -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. + */ + +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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'non-void-return-type.ets'), +]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() 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); +} +@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\"); +} +@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); +} +@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; + } + return __memo_scope.recache({ + str: \"1\", + }); +} +@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) => {})); +} +@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\")); +} +@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; + } + return __memo_scope.recache((() => {})); +} +interface A { + set str(str: string) + get str(): string +} +type B = ((str: string)=> void); +class C { + public str: string; + public constructor(str: string) { + this.str = str; + } +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform functions with non-void return type', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..eca62211fc62871f30dcbfc225feb97dc0c82677 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'type-reference.ets'), +]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() 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 => { + 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 { + set item(item: T) + get item(): T +} +interface Attribute { + @memo() each(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() itemGenerator: ItemBuilder): Attribute +} +class B { + @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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_ri = __memo_scope.param(0, ri); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform functions with type reference', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..fd2f3cb8885fe73b2a58e68377b065d63ca14a04 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const FUNCTION_DIR_PATH: string = 'memo/functions'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'void-return-type.ets'), +]; + +const pluginTester = new PluginTester('test memo function', buildConfig); + +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() 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; + return; + } + { + __memo_scope.recache(); + return; + } +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform functions with void return type', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..18b3a26e3c2fdeeca0190011d9eb903b497f1a4b --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const LAMBDA_DIR_PATH: string = 'memo/lambdas'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAMBDA_DIR_PATH, 'argument-call.ets')]; + +const pluginTester = new PluginTester('test memo lambda', buildConfig); + +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() {} +((arg: (()=> void)) => {})((() => {})); + +((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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (201676739)), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + 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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (209782503)), 0); + 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; + } + @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; + return; + } + { + __memo_scope.recache(); + return; + } + }) as @memo() ((__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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })(__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; + } + })); + { + __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 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); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }); + goo(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } +}); + +(() => { + 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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }) 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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); +}); +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform argument calls in lambdas', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..60d5c5985a778d9007d1fda091eeccd099ccc8d8 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { Plugins } from '../../../../common/plugin-context'; +import { uiTransform } from '../../../../ui-plugins'; + +const LAMBDA_DIR_PATH: string = 'memo/lambdas'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAMBDA_DIR_PATH, 'function-with-receiver.ets'), +]; + +const parsedTransform: Plugins = { + name: 'state-complex-type', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test memo lambda', buildConfig); + +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() 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) { + __memo_scope.cached; + return; + } + console.log("Good", __memo_parameter_str.value); + { + __memo_scope.recache(); + return; + } +} + +@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) { + return __memo_scope.cached; + } + console.log("Good", __memo_parameter_str.value); + return __memo_scope.recache(this); +} + +class 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; + } + foo1(this, __memo_context, ((__memo_id) + (119664703)), "morning"); + return __memo_scope.recache(foo2(this, __memo_context, ((__memo_id) + (181969214)), "afternoon")); + } + + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform lambdas about function with receiver feature', + [parsedTransform, beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..b2465acc7af6249c504a615323acf7e4e6e429a0 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const LAMBDA_DIR_PATH: string = 'memo/lambdas'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAMBDA_DIR_PATH, 'trailing-lambdas.ets'), +]; + +const pluginTester = new PluginTester('test memo lambda', buildConfig); + +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() ((__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 A(); + a.foo(__memo_context, ((__memo_id) + ()), (() => { + console.log(); + })); + 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; + return; + } + console.log(); + { + __memo_scope.recache(); + return; + } + })); + 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; + return; + } + console.log(); + { + __memo_scope.recache(); + return; + } + })); + bar(__memo_context, ((__memo_id) + ()), (() => { + console.log(); + })); + 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; + return; + } + console.log(); + { + __memo_scope.recache(); + return; + } + })); + 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; + return; + } + console.log(); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +}); +@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; + } +} +class A { + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + 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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform trailing lambdas', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..37b1c13655ec89a611c7d49d6fc1f13b28810d15 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const LAMBDA_DIR_PATH: string = 'memo/lambdas'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAMBDA_DIR_PATH, 'void-lambda.ets'), +]; + +const pluginTester = new PluginTester('test memo lambda', buildConfig); + +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() ((__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; + } + { + __memo_scope.recache(); + return; + } +}); +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg?: (()=> string)): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_arg = __memo_scope.param(0, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +}); +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform lambdas with void return type', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..748b1dd4b520b4881721b271ec8c6fedc6884833 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const LAMBDA_DIR_PATH: string = 'memo/lambdas'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, LAMBDA_DIR_PATH, 'with-receiver.ets'), +]; + +const pluginTester = new PluginTester('test memo lambda', buildConfig); + +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() ((__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_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; + return; + } + { + __memo_scope.recache(); + return; + } + })); + let f1: F1 = foo; + let f2: F2 = goo; + let a = new A(); + 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; + return; + } + { + __memo_scope.recache(); + return; + } + })); + 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; + return; + } + { + __memo_scope.recache(); + return; + } + })); + 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; + return; + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } +}); + +@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; + } +} + +class Person { + public constructor() {} +} + +class A { + public constructor() {} +} + +@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 { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform lambdas with receiver', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..f56a4c7cf0253c6502ce49436f73e8021dcd995b --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const METHOD_DIR_PATH: string = 'memo/methods'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'argument-call.ets')]; + +const pluginTester = new PluginTester('test memo method', buildConfig); + +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() {} +class Test { + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + __memo_parameter_content.value(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +class Use { + @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; + return; + } + const test = new Test(); + test.lambda_arg(__memo_context, ((__memo_id) + ()), ((__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; + } + { + __memo_scope.recache(); + return; + } + })); + test.lambda_arg_with_arg(__memo_context, ((__memo_id) + ()), ((__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_value = __memo_scope.param(0, value); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + 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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }), (() => {}), (() => {})); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform argument calls in methods', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..c611a8e99dcd1ac328a65bc6a48a66103cc53a5a --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts @@ -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. + */ + +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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const METHOD_DIR_PATH: string = 'memo/methods'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'callable.ets')]; + +const pluginTester = new PluginTester('test memo method', buildConfig); + +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() ((__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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + let x: (C | D) = C.$_instantiate(__memo_context, ((__memo_id) + ()), (() => { + return new C(); + })); + x = D.$_instantiate((() => { + return new D(); + })); + { + __memo_scope.recache(); + return; + } +}); +class A { + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +class B { + public static $_invoke(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + public constructor() {} +} +class 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) { + return __memo_scope.cached; + } + return __memo_scope.recache(__memo_parameter_factory.value()); + } + public constructor() {} +} +class D { + public static $_instantiate(factory: (()=> D), @memo() content?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): D { + return factory(); + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform callable class', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..d1e6012ea7cdde4b5b9293f6336e1a4abfd535f4 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const METHOD_DIR_PATH: string = 'memo/methods'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'declare-and-call.ets'), +]; + +const pluginTester = new PluginTester('test memo method', buildConfig); + +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() ((__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; + } + new AA().x(__memo_context, ((__memo_id) + ())); + const a: A = new AA(); + a.x(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } +}); +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 constructor() {} +} +class AA extends A { + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform declare methods and calls', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..caf2b1b9fc3addf9a3e03f33f6be9cb5b60fe728 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 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 projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', 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\"; +function main() {} +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 { + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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; + return; + } + this.void_method(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } + @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; + } + __memo_context; + __memo_id; + { + __memo_scope.recache(); + return; + } + } + public memo_lambda() { + @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; + } + }); + } + @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; + return; + } + @memo() const f = ((__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(123); + }), g = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number): number => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_x = __memo_scope.param(0, x); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + return __memo_scope.recache(((123) + (__memo_parameter_x.value))); + }); + const h = @memo() ((__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); + }); + f(__memo_context, ((__memo_id) + ())); + g(__memo_context, ((__memo_id) + ()), 1); + h(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } + @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; + }) as (()=> int))); + let arg3: int = (((gensym%%_3) !== (undefined)) ? gensym%%_3 : (arg1 as int)); + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 4); + 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); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + console.log(__memo_parameter_arg1.value, __memo_parameter_arg2.value, __memo_parameter_arg3.value, __memo_parameter_arg4.value); + console.log(__memo_parameter_arg2.value()); + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + console.log(__memo_parameter_arg1.value); + console.log(__memo_parameter_arg2.value); + console.log(({let gensym%%_166 = __memo_parameter_arg2.value; + (((gensym%%_166) == (null)) ? undefined : gensym%%_166())})); + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + __memo_parameter_arg.value(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform inner calls in methods', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..8957ca871ad3e5e0da09ea031a439130286396e6 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.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, 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 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 projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', 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\"; +function main() {} +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 { + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + return __memo_scope.cached; + } + return __memo_scope.recache(__memo_parameter_arg.value); + } + @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) { + return __memo_scope.cached; + } + return __memo_scope.recache(__memo_parameter_arg.value); + } + @memo_intrinsic() public intrinsic_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): int { + return 0; + } + @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; + } + @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 __memo_context; + }); + const getId = (() => { + return __memo_id; + }); + { + const __memo_context = getContext(); + const __memo_id = getId(); + 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 { + @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; + return; + } + const test = new Test(); + test.string_method_with_return(__memo_context, ((__memo_id) + ()), "a string"); + test.method_with_type_parameter(__memo_context, ((__memo_id) + ()), "I'm string"); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform methods with non-void return type', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..41e73d5bb6563d50dc70a623b274578e99c570a5 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const METHOD_DIR_PATH: string = 'memo/methods'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'void-method.ets'), +]; + +const pluginTester = new PluginTester('test memo method', buildConfig); + +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() {} +class A { + public x: int; + public y: int; + public constructor() {} +} +class Test { + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + @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) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +class Use { + @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; + return; + } + const test = new Test(); + test.void_method(__memo_context, ((__memo_id) + ())); + test.void_method_with_arg(__memo_context, ((__memo_id) + ()), "an arg"); + test.void_method_with_return(__memo_context, ((__memo_id) + ()), "a value"); + Test.static_method_with_type_parameter(__memo_context, ((__memo_id) + ()), "I'm static"); + test.obj_arg(__memo_context, ((__memo_id) + ()), { + x: 1, + y: 2, + }); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform methods with void return type', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..b3be707bacd797ed55de8322d0854c2c26cf204d --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const PROPERTY_DIR_PATH: string = 'memo/properties'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, PROPERTY_DIR_PATH, 'class-constructor.ets'), +]; + +const pluginTester = new PluginTester('test memo property', buildConfig); + +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() ((__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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }), + }); + { + __memo_scope.recache(); + return; + } +}); +interface A { + @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); + constructor() { + this(undefined); + } + public constructor(arg: (A | undefined)) { + this.a = ({let gensym%%_ = arg; + (((gensym%%_) == (null)) ? undefined : gensym%%_.a)}); + } + @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; + } + ({let gensym%%_ = this.a; + (((gensym%%_) == (null)) ? undefined : gensym%%_(__memo_context, ((__memo_id) + ())))}); + { + __memo_scope.recache(); + return; + } + } +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform properties in class constructor', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..1a97511af702bcfdf1e613dbc950f2081d37d235 --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.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 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const PROPERTY_DIR_PATH: string = 'memo/properties'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, PROPERTY_DIR_PATH, 'class-properties.ets'), +]; + +const pluginTester = new PluginTester('test memo property', buildConfig); + +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() {} +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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }); + 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) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + }); + 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; + 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; + } + this.arg(); + this.memo_arg(__memo_context, ((__memo_id) + ())); + this.arg_memo_type(__memo_context, ((__memo_id) + ())); + { + __memo_scope.recache(); + return; + } + } +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform properties in class', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..a28664fde2474320eb0656d51a88c3ef73b1cb8c --- /dev/null +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const PROPERTY_DIR_PATH: string = 'memo/properties'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, PROPERTY_DIR_PATH, 'interfaces.ets')]; + +const pluginTester = new PluginTester('test memo property', buildConfig); + +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() ((__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: A = { + arg: (() => {}), + 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; + return; + } + { + __memo_scope.recache(); + return; + } + }), + 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; + return; + } + { + __memo_scope.recache(); + return; + } + }), + 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; + return; + } + { + __memo_scope.recache(); + return; + } + }), + }; + { + __memo_scope.recache(); + return; + } +}); +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)) + @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)) + @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) +} +`; + +function testMemoTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform interface properties', + [beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0f5ac29d50499a8173da1d11a6ccb61832c8924 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.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 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 ANIMATION_DIR_PATH: string = 'animation'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, ANIMATION_DIR_PATH, 'animatable-extend-basic.ets'), +]; + +const animatableExtendTransform: Plugins = { + name: 'animatable-extend', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test basic animatableExtend transform', buildConfig); + +const expectedScript: string = ` + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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"; + +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() 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 {} + + @memo() public build() { + Column(undefined, undefined, (() => { + 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); + })); + } + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + AnimatablePropertyExample._instantiateImpl(undefined, (() => { + return new AnimatablePropertyExample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +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 animation transform', + [animatableExtendTransform, uiNoRecheck, recheck], + { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..ac52cd1e5823253a82f4eeb27ca53abfebb99c25 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.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 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 ANIMATION_DIR_PATH: string = 'animation'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, ANIMATION_DIR_PATH, 'animation-basic.ets'), +]; + +const animationTransform: Plugins = { + name: 'animation', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test basic animation transform', buildConfig); + +const expectedScript: string = ` + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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"; + +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() 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 {} + + @memo() public build() { + Column(undefined, undefined, (() => { + 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); + })); + } + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + AnimatablePropertyExample._instantiateImpl(undefined, (() => { + return new AnimatablePropertyExample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +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, recheck], + { + checked: [testAnimationTransformer], + }, + { + stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.common'] }, + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..2f3702d00dc6a790f492c9bb0691191b4834e9df --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; +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' + ), +]; + +const pluginTester = new PluginTester('test custom component call transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'custom-component-call', + parsed: uiTransform().parsed, +}; + +const expectedParsedScript: string = ` + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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 struct CustomContainer extends CustomComponent { + @Builder() public closerBuilder() {} + + @BuilderParam() public closer: (()=> void) = this.closerBuilder; + + public build() {} + + public constructor() {} + +} + +@Component() final struct CustomContainerUser extends CustomComponent { + public build() { + Column(){ + CustomContainer(){ + Column(){ + Text("hello"); + }; + }; + CustomContainer({}){ + Column(){}; + }; + CustomContainer(undefined){}; + CustomContainer(); + }; + } + + public constructor() {} + +} + +@Component() export interface __Options_CustomContainer { + @BuilderParam() closer?: (()=> void); + +} + +@Component() export interface __Options_CustomContainerUser { + +} +`; + +function testParedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedParsedScript)); +} + +const expectedBuilderLambdaScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 {} + + private __backing_closer?: @memo() (()=> void); + + public get closer(): @memo() (()=> void) { + return this.__backing_closer!; + } + + public set closer(value: @memo() (()=> void)) { + this.__backing_closer = value; + } + + @memo() public closerBuilder() {} + + @memo() public build() {} + + private constructor() {} + +} + +@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, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Text(undefined, "hello", undefined, undefined); + })); + })); + CustomContainer._instantiateImpl(undefined, (() => { + return new CustomContainer(); + }), {}, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => {})); + })); + CustomContainer._instantiateImpl(undefined, (() => { + return new CustomContainer(); + }), undefined, undefined, @memo() (() => {})); + CustomContainer._instantiateImpl(undefined, (() => { + return new CustomContainer(); + }), undefined, undefined, undefined); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_CustomContainer { + set closer(closer: (@memo() (()=> void) | undefined)) + + get closer(): (@memo() (()=> void) | undefined) + +} + +@Component() export interface __Options_CustomContainerUser { + +} +`; + +function testCustomComponentTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedBuilderLambdaScript)); +} + +pluginTester.run( + 'test custom component call transformation', + [parsedTransform, recheck, uiNoRecheck, recheck], + { + parsed: [testParedTransformer], + 'checked:ui-no-recheck': [testCustomComponentTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-content.test.ts deleted file mode 100644 index 0b4e71c2dd08260bd613d49c12d6c4814d98273b..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-content.test.ts +++ /dev/null @@ -1,133 +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 path from 'path'; -import * as arkts from '@koalaui/libarkts'; -import { PluginTestContext, 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 { PluginContext, Plugins } from '../../../../common/plugin-context'; -import { ComponentTransformer } from '../../../../ui-plugins/component-transformer'; -import { BuilderLambdaTransformer } from '../../../../ui-plugins/builder-lambda-translators/builder-lambda-transformer'; - -const moduleRootPath: string = path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda'); - -const instantiateTransform: Plugins = { - name: 'instantiate', - parsed(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const componentTransformer = new ComponentTransformer({ - arkui: '@koalaui.arkts-arkui.StructBase', - }); - script = componentTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); - return script; - } - }, - checked(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const builderLambdaTransformer = new BuilderLambdaTransformer(); - script = builderLambdaTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); - return script; - } - }, -}; - -const pluginTester = new PluginTester('test build-lambda transformer'); - -// Test single-content instantiateImpl transformation -function testSingleContent(this: PluginTestContext): void { - const script: arkts.EtsScript = this.script as arkts.EtsScript; - const expectedScript: string = ` -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Component as Component } from "@koalaui.arkts-arkui.Common"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - -} - -final class MyStateSample extends StructBase { - public build() { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello!"); - })); - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - -`; - expect(parseDumpSrc(script.dumpSrc())).toBe(parseDumpSrc(expectedScript)); -} - -pluginTester.run( - 'transform $_instantiate for single component content', - [instantiateTransform], - { - 'checked:instantiate': [testSingleContent], - }, - { - stopAfter: 'checked', - buildConfig: { - ...mockBuildConfig(), - compileFiles: [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda', 'instantiate-content.ets'), - ], - moduleRootPath, - }, - }, - { - beforeEach: [ - () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }, - ], - afterEach: [ - () => { - jest.spyOn(console, 'warn').mockRestore(); - }, - ], - } -); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-multi-content.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-multi-content.test.ts deleted file mode 100644 index 34c169437fd9153ac5a3c6a8cc6a0770d12a93b6..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate-multi-content.test.ts +++ /dev/null @@ -1,140 +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 path from 'path'; -import * as arkts from '@koalaui/libarkts'; -import { PluginTestContext, 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 { PluginContext, Plugins } from '../../../../common/plugin-context'; -import { ComponentTransformer } from '../../../../ui-plugins/component-transformer'; -import { BuilderLambdaTransformer } from '../../../../ui-plugins/builder-lambda-translators/builder-lambda-transformer'; - -const moduleRootPath: string = path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda'); - -const instantiateTransform: Plugins = { - name: 'instantiate', - parsed(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const componentTransformer = new ComponentTransformer({ - arkui: '@koalaui.arkts-arkui.StructBase', - }); - script = componentTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); - return script; - } - }, - checked(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const builderLambdaTransformer = new BuilderLambdaTransformer(); - script = builderLambdaTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); - return script; - } - }, -}; - -const pluginTester = new PluginTester('test build-lambda transformer'); - -// Test multi-content instantiateImpl transformation -function testMultiContent(this: PluginTestContext): void { - const script: arkts.EtsScript = this.script as arkts.EtsScript; - const expectedScript: string = ` -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Component as Component } from "@koalaui.arkts-arkui.Common"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -import { Text as Text } from "@koalaui.arkts-arkui.Text"; - -import { Button as Button } from "@koalaui.arkts-arkui.Button"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - -} - -final class MyStateSample extends StructBase { - public build() { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => { - Text.instantiateImpl(((instance: Text): Text => { - return instance; - }), ((): Text => { - return new Text(); - }), "Hello!"); - Button.instantiateImpl(((instance: Button): Button => { - return instance; - }), ((): Button => { - return new Button(); - }), "click me"); - })); - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - -`; - expect(parseDumpSrc(script.dumpSrc())).toBe(parseDumpSrc(expectedScript)); -} - -pluginTester.run( - 'transform $_instantiate for multiple component contents', - [instantiateTransform], - { - 'checked:instantiate': [testMultiContent], - }, - { - stopAfter: 'checked', - buildConfig: { - ...mockBuildConfig(), - compileFiles: [ - path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda', 'instantiate-multi-content.ets'), - ], - moduleRootPath, - }, - }, - { - beforeEach: [ - () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }, - ], - afterEach: [ - () => { - jest.spyOn(console, 'warn').mockRestore(); - }, - ], - } -); diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate.test.ts deleted file mode 100644 index 3a54f332bbafbe40b028af1488367d97cc38af58..0000000000000000000000000000000000000000 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/instantiate.test.ts +++ /dev/null @@ -1,120 +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 path from 'path'; -import * as arkts from '@koalaui/libarkts'; -import { PluginTestContext, PluginTester } from '../../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../../utils/artkts-config'; -import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; -import { parseDumpSrc } from '../../../utils/parse-string'; -import { PluginContext, Plugins } from '../../../../common/plugin-context'; -import { ComponentTransformer } from '../../../../ui-plugins/component-transformer'; -import { BuilderLambdaTransformer } from '../../../../ui-plugins/builder-lambda-translators/builder-lambda-transformer'; - -const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda', 'instantiate.ets')]; -buildConfig.moduleRootPath = path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'builder-lambda'); - -const instantiateTransform: Plugins = { - name: 'instantiate', - parsed(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const componentTransformer = new ComponentTransformer({ - arkui: '@koalaui.arkts-arkui.StructBase', - }); - script = componentTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); - return script; - } - }, - checked(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - - const builderLambdaTransformer = new BuilderLambdaTransformer(); - script = builderLambdaTransformer.visitor(script) as arkts.EtsScript; - - arkts.setAllParents(script); - return script; - } - }, -}; - -const pluginTester = new PluginTester('test build-lambda transformer', buildConfig); - -function testInstantiateImpl(this: PluginTestContext): void { - const script: arkts.EtsScript = this.script as arkts.EtsScript; - const expectedScript: string = ` -import { StructBase as StructBase } from "@koalaui.arkts-arkui.StructBase"; - -import { Component as Component } from "@koalaui.arkts-arkui.Common"; - -import { Column as Column } from "@koalaui.arkts-arkui.Column"; - -abstract class ETSGLOBAL { - public static main() {} - - public static _$init$_() {} - - -} - -final class MyStateSample extends StructBase { - public build() { - Column.instantiateImpl(((instance: Column): Column => { - return instance; - }), ((): Column => { - return new Column(); - }), (() => {})); - } - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - -`; - expect(parseDumpSrc(script.dumpSrc())).toBe(parseDumpSrc(expectedScript)); -} - -pluginTester.run( - 'transform $_instantiate for a single component', - [instantiateTransform], - { - 'checked:instantiate': [testInstantiateImpl], - }, - { - stopAfter: 'checked', - }, - { - beforeEach: [ - () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }, - ], - afterEach: [ - () => { - jest.spyOn(console, 'warn').mockRestore(); - }, - ], - } -); 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 new file mode 100644 index 0000000000000000000000000000000000000000..e8e1ace16621ce77238c023b35f9aec5457e3673 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.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 { beforeMemoNoRecheck, builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'simple-component.ets'), +]; + +const pluginTester = new PluginTester('test builder-lambda simple component', buildConfig); + +function testBuilderLambdaTransformer(this: PluginTestContext): void { + const expectedScript: string = ` +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, @memo() (() => {})); + } + public constructor() {} +} +`; + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +function testMemoTransformer(this: PluginTestContext): void { + 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"; +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(__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() ((__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; + return; + } + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + public constructor() {} +} +`; + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'transform simple component', + [builderLambdaNoRecheck, beforeMemoNoRecheck, memoNoRecheck, recheck], + { + 'checked:builder-lambda-no-recheck': [testBuilderLambdaTransformer], + 'checked:memo-no-recheck': [testMemoTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..8e2c2dd05d7ff77e52ab79cbbd288f719d6fbc3e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'style-with-receiver.ets'), +]; + +const pluginTester = new PluginTester('test function with receiver style transformstion', buildConfig); + +const parsedTransform: Plugins = { + name: 'style-with-receiver', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { memo as memo } from "@ohos.arkui.stateManagement"; + +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: TextAttribute, num: number, str: string): TextAttribute { + this.fontSize(num); + this.backgroundColor(num); + return this; +} + +@memo() function style22(this: TextAttribute): TextAttribute { + this.fontWeight(700); + return this; +} + + +@Component() final struct MM extends CustomComponent { + public __initializeStruct(initializers: (__Options_MM | undefined), @memo() content: ((()=> void) | undefined)): void {} + + public __updateStruct(initializers: (__Options_MM | undefined)): 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: TextAttribute): void => { + cardStyle(instance, 600, "#eeeeee"); + return; + }), "hello world", undefined, undefined); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_MM { + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test function with receiver style transformstion', + [parsedTransform, uiNoRecheck, recheck], + { + '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..1661bfbde62eca7187cab7ce569be708683ed864 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..fc931a33162d00f5023afac63ca2e321136b588f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/component/for-each.test.ts @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); + })); + })); + } + + private 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 new file mode 100644 index 0000000000000000000000000000000000000000..c95268bf16885e28c5f5835a3e0325726b1f55e5 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 'builder-param-passing.ets'), +]; + +const pluginTester = new PluginTester('test builder param variable passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'builder-param-passing', + parsed: uiTransform().parsed, +}; + +const expectedAfterUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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: ((()=> 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() (()=> void); + + public get customBuilderParam(): @memo() (()=> void) { + return this.__backing_customBuilderParam!; + } + + public set customBuilderParam(value: @memo() (()=> void)) { + this.__backing_customBuilderParam = value; + } + + @memo() public customBuilder() {} + + @memo() public build() { + this.customBuilderParam(); + } + + private 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(); + }), { + customBuilderParam: this.componentBuilder, + }, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + customBuilderParam: @memo() (() => { + this.componentBuilder(); + }), + }, undefined, undefined); + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, @memo() (() => { + Text(undefined, "Parent builder", undefined, undefined); + })); + })); + } + + private 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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; + } + } + + private 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; + } + } + + private constructor() {} + +} + +@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) + +} + +@Component() export interface __Options_Parent { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + 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, 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 new file mode 100644 index 0000000000000000000000000000000000000000..814b8c3aee295c3d01fb7c663f1a68bab416355c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 'init-with-local-builder.ets'), +]; + +const pluginTester = new PluginTester('test builder param init with local builder', buildConfig); + +const parsedTransform: Plugins = { + name: 'init-with-local-builder', + parsed: uiTransform().parsed +}; + +const expectedAfterUIScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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: ((()=> 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() (()=> void); + + public get customBuilderParam(): @memo() (()=> void) { + return this.__backing_customBuilderParam!; + } + + 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(value: @memo() ((str: string)=> void)) { + this.__backing_customBuilderParam2 = value; + } + + @memo() public doNothingBuilder() {} + + @memo() public doNothingBuilder2(str: string) {} + + @memo() public build() { + this.customBuilderParam(); + this.customBuilderParam2("hello"); + } + + private 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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; + } + } + + private constructor() {} + +} + +@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(expectedAfterUIScript)); +} + +function testAfterMemoCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedAfterMemoScript)); +} + +pluginTester.run( + 'test builder param init with local builder', + [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..c16283a4ef9fa1f197f59292bd161444a6c59884 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/optional-builder-param.test.ts @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { memo as memo } from "arkui.stateManagement.runtime"; +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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?: (@memo() (()=> 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() (() => { + if (this.customBuilderParam2) { + (this.customBuilderParam2 as (()=> void))(); + } + if (this.customBuilderParam2) { + this.customBuilderParam2!(); + } + this.customBuilderParam1(); + })); + } + + private 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); + })); + } + + private 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 { memo as memo } from "arkui.stateManagement.runtime"; +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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() ((__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; + } + if (this.customBuilderParam2) { + (this.customBuilderParam2 as ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))(__memo_context, ((__memo_id) + (241913892))); + } + if (this.customBuilderParam2) { + this.customBuilderParam2!(__memo_context, ((__memo_id) + (137225318))); + } + this.customBuilderParam1(__memo_context, ((__memo_id) + (211301233))); + { + __memo_scope.recache(); + return; + } + })); + { + __memo_scope.recache(); + return; + } + } + + private 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; + } + } + + private 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 new file mode 100644 index 0000000000000000000000000000000000000000..3ecead48626553fc65ae98091666d645454816d7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +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, +}; + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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, @memo() (() => { + Text(undefined, (("UseStateVarByReference: ") + (params.paramA1)), undefined, undefined); + })); +} + + +class Tmp { + public paramA1: string = ""; + + public constructor() {} + +} + +@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({ + paramA1: "Hello", + }); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_BuilderDemo { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'global builder', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); 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 new file mode 100644 index 0000000000000000000000000000000000000000..318d1ee717f7352f70b154979ad33c5291a79957 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/builder'; + +const buildConfig: BuildConfig = mockBuildConfig(); +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, +}; + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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: TextAttribute): void => { + instance.fontSize(30); + return; + }), "Hello World", undefined, undefined); + } + + @memo() public showTextValueBuilder(param: string) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), param, undefined, undefined); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + this.showTextBuilder(); + this.showTextValueBuilder("Hello @Builder"); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_BuilderDemo { + +} +`; + +function testCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'local builder', + [parsedTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..a270de494e3f096d057bda0a42a630d9006e8150 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-basic-type.test.ts @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/link'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'link-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Link decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'link-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 { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = 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 = 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 = 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 = 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 = STATE_MGMT_FACTORY.makeLink(this, "linkVar5", initializers!.__backing_linkVar5!); + }; + } + + 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?: ILinkDecoratedVariable; + + public get linkVar2(): number { + return this.__backing_linkVar2!.get(); + } + + public set linkVar2(value: number) { + this.__backing_linkVar2!.set(value); + } + + 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?: ILinkDecoratedVariable; + + public get linkVar4(): undefined { + return this.__backing_linkVar4!.get(); + } + + public set linkVar4(value: undefined) { + this.__backing_linkVar4!.set(value); + } + + 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() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Link decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..7cea19daeb4d0ca1c5b42a08e9a365f6334fe56a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-complex-type.test.ts @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/link'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'link-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Link decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'link-complex-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 { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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]))) { + return LinkType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant LinkType.") + (name))); + } + + public static fromValue(value: int): LinkType { + for (let i = 0;((i) < (LinkType.#ValuesArray.length));(++i)) { + if (((value) == (LinkType.#ValuesArray[i]))) { + return LinkType.#ItemsArray[i]; + } + } + 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() 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = STATE_MGMT_FACTORY.makeLink<(Set | Per)>(this, "linkVar12", initializers!.__backing_linkVar12!); + }; + } + + 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?: ILinkDecoratedVariable>; + + public get linkVar2(): Array { + return this.__backing_linkVar2!.get(); + } + + public set linkVar2(value: Array) { + this.__backing_linkVar2!.set(value); + } + + 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?: ILinkDecoratedVariable>; + + public get linkVar4(): Set { + return this.__backing_linkVar4!.get(); + } + + public set linkVar4(value: Set) { + this.__backing_linkVar4!.set(value); + } + + 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?: ILinkDecoratedVariable>; + + public get linkVar6(): Array { + return this.__backing_linkVar6!.get(); + } + + public set linkVar6(value: Array) { + this.__backing_linkVar6!.set(value); + } + + 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?: 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?: ILinkDecoratedVariable; + + public get linkVar9(): Date { + return this.__backing_linkVar9!.get(); + } + + public set linkVar9(value: Date) { + this.__backing_linkVar9!.set(value); + } + + 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?: ILinkDecoratedVariable<(string | number)>; + + public get linkVar11(): (string | number) { + return this.__backing_linkVar11!.get(); + } + + public set linkVar11(value: (string | number)) { + this.__backing_linkVar11!.set(value); + } + + private __backing_linkVar12?: ILinkDecoratedVariable<(Set | Per)>; + + public get linkVar12(): (Set | Per) { + return this.__backing_linkVar12!.get(); + } + + public set linkVar12(value: (Set | Per)) { + this.__backing_linkVar12!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Link decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..a507ebd52061f906a5d636684d07045d6f6eef01 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/link'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'link-to-link-prop-state.ets'), +]; + +const pluginTester = new PluginTester('test @Link decorated variables passing to other variables', buildConfig); + +const parsedTransform: Plugins = { + name: 'link-to-link-prop-state', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = STATE_MGMT_FACTORY.makeLink(this, "text1", initializers!.__backing_text1!); + }; + } + + 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() { + 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, + }, undefined, undefined); + })); + } + + private constructor() {} + +} + +@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 = STATE_MGMT_FACTORY.makeLink(this, "childText", initializers!.__backing_childText!); + }; + this.__backing_childText2 = STATE_MGMT_FACTORY.makeState(this, "childText2", ((({let gensym___95513066 = initializers; + (((gensym___95513066) == (null)) ? undefined : gensym___95513066.childText2)})) ?? ("sss"))); + 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 { + if (((({let gensym___77632518 = initializers; + (((gensym___77632518) == (null)) ? undefined : gensym___77632518.childText3)})) !== (undefined))) { + this.__backing_childText3!.update((initializers!.childText3 as string)); + } + if (((({let gensym___250510741 = initializers; + (((gensym___250510741) == (null)) ? undefined : gensym___250510741.childText4)})) !== (undefined))) { + this.__backing_childText4!.update((initializers!.childText4 as string)); + } + } + + 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?: IStateDecoratedVariable; + + public get childText2(): string { + return this.__backing_childText2!.get(); + } + + public set childText2(value: string) { + this.__backing_childText2!.set(value); + } + + 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?: IPropDecoratedVariable; + + public get childText4(): string { + return this.__backing_childText4!.get(); + } + + public set childText4(value: string) { + this.__backing_childText4!.set(value); + } + + @memo() public build() { + TextInput(undefined, { + text: this.childText, + }, undefined); + } + + private constructor() {} + +} + +@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) + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Link decorated variables passing to other variables', + [parsedTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..111874d8cab45adbff9973641a6ffbb6de9d8674 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/link'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-link.ets'), +]; + +const pluginTester = new PluginTester('test @Link decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'state-to-link', + 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 { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + +__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 = STATE_MGMT_FACTORY.makeLink(this, "selectedDate", initializers!.__backing_selectedDate!); + }; + } + + 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() { + 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: ButtonAttribute): void => { + instance.margin(10).onClick(((e: ClickEvent) => { + this.selectedDate = new Date("2023-09-09"); + })); + return; + }), "child update the new date", undefined, undefined); + DatePicker(undefined, { + start: new Date("1970-1-1"), + end: new Date("2100-1-1"), + selected: this.selectedDate, + }, undefined); + })); + } + + private constructor() {} + +} + +@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?: IStateDecoratedVariable; + + public get parentSelectedDate(): Date { + return this.__backing_parentSelectedDate!.get(); + } + + public set parentSelectedDate(value: Date) { + this.__backing_parentSelectedDate!.set(value); + } + + @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: ButtonAttribute): void => { + instance.margin(10).onClick(((e: ClickEvent) => { + this.parentSelectedDate = new Date("2023-07-07"); + })); + return; + }), "parent update the new date", undefined, undefined); + DatePicker(undefined, { + start: new Date("1970-1-1"), + end: new Date("2100-1-1"), + selected: this.parentSelectedDate, + }, undefined); + DateComponent._instantiateImpl(undefined, (() => { + return new DateComponent(); + }), { + __backing_selectedDate: this.__backing_parentSelectedDate, + }, undefined, undefined); + })); + } + + private constructor() {} + +} + +@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) + +} + +@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 { + @memo() public entry(): void { + ParentComponent._instantiateImpl(undefined, (() => { + return new ParentComponent(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Link decorated variables passing', + [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..b211f503cec6b143cd3ef5afe92eed2bd52f72f2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-complex-type.test.ts @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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], Type.from>()) + this.__backing_objectA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "objectA", {}, Type.from()) + this.__backing_dateA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "dateA", new Date("2021-08-08"), Type.from()) + this.__backing_setA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop4", "setA", new Set(), Type.from>()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeLocalStorageLink>(this, "Prop5", "mapA", new Map(), Type.from>()) + this.__backing_classA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop7", "classA", new Person("John"), Type.from()) + this.__backing_enumA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop8", "enumA", Status.NotFound, Type.from()) + } + + 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() {} + + private 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..8b40cd45d21e2fca1e3419cd0581db0aeafad549 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/localstoragelink/localstoragelink-primitive-type.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 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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, Type.from()) + this.__backing_stringA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop2", "stringA", "AA", Type.from()) + this.__backing_booleanA = STATE_MGMT_FACTORY.makeLocalStorageLink(this, "Prop3", "booleanA", true, Type.from()) + } + + 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() {} + + private 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/objectlink/objectlink-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d832bf9762771c084e587b4bdb392468199044db --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-basic.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 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + +} + +@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)})!) + } + + 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() {} + + private 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) + +} +`; + +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..aef8d5ccb2c8754fe1fb27991b780f98354d9506 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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)})!) + } + + 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); + })); + } + + private 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); + })); + } + + private 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..0f5144ec94227de65aefdf9ea20fca86615d0da9 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonrename.test.ts @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + + private 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..3f35028ec6e52c52186ca4b11d6398c57c71a500 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-jsonstringifyignore.test.ts @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + + private 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 new file mode 100644 index 0000000000000000000000000000000000000000..b36744bcdae7a48234894f868bbd3420de1b052c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-only.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 { 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-only.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed only transform', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed } 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(); + + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; + + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; + + public constructor() {} + + public get propA(): number { + 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"); + } + } + + public get trackA(): number { + 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"); + } + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed only transform', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..e5d7b06cd9ff714b7b1061cc1fa66efdbf1af5d0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-class-property.test.ts @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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-track-class-property.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed track transform with class property', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + + + +class Info { + public constructor() {} + +} + +class E 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 propE: Info = new Info(); + + @JSONRename({newName:"trackE"}) private __backing_trackE: Info = new Info(); + + @JSONStringifyIgnore() private __meta_trackE: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get trackE(): Info { + this.conditionalAddRef(this.__meta_trackE); + return this.__backing_trackE; + } + + public set trackE(newValue: Info) { + if (((this.__backing_trackE) !== (newValue))) { + this.__backing_trackE = newValue; + this.__meta_trackE.fireChange(); + this.executeOnSubscribingWatches("trackE"); + } + } + +} + +@Observed() class E1 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:"propE1"}) private __backing_propE1: Info = new Info(); + + @JSONRename({newName:"trackE1"}) private __backing_trackE1: Info = new Info(); + + public constructor() {} + + public get propE1(): Info { + this.conditionalAddRef(this.__meta); + return this.__backing_propE1; + } + + public set propE1(newValue: Info) { + if (((this.__backing_propE1) !== (newValue))) { + this.__backing_propE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propE1"); + } + } + + public get trackE1(): Info { + this.conditionalAddRef(this.__meta); + return this.__backing_trackE1; + } + + public set trackE1(newValue: Info) { + if (((this.__backing_trackE1) !== (newValue))) { + this.__backing_trackE1 = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackE1"); + } + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed track transform with class property', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..9f2d7360aebd5519e0dfba58860eafcf71d10ad3 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts @@ -0,0 +1,852 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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-track-complex-type.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed track transform with complex type', 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 { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { Observed as Observed, Track as Track } from "@ohos.arkui.stateManagement"; + +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() {} + +} + +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(); + } + +} + +@Observed() 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); + } + + @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:"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 constructor() {} + + public get numA(): number { + 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"); + } + } + + public get stringA(): string { + 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"); + } + } + + public get booleanA(): boolean { + 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"); + } + } + + public get arrayA(): Array { + this.conditionalAddRef(this.__meta_arrayA); + return 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 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 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 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 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 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 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 this.__backing_enumA; + } + + public set enumA(newValue: Status) { + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); + } + } + +} + +@Observed() class mixed2 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:"numA"}) private __backing_numA: number = 33; + + @JSONRename({newName:"stringA"}) private __backing_stringA: string = "AA"; + + @JSONRename({newName:"booleanA"}) private __backing_booleanA: boolean = true; + + @JSONRename({newName:"arrayA"}) private __backing_arrayA: Array = [1, 2, 3]; + + @JSONRename({newName:"objectA"}) private __backing_objectA: Object = {}; + + @JSONRename({newName:"dateA"}) private __backing_dateA: Date = new Date("2021-08-08"); + + @JSONRename({newName:"setA"}) private __backing_setA: Set = new Set(); + + @JSONRename({newName:"mapA"}) private __backing_mapA: Map = new Map(); + + @JSONRename({newName:"unionA"}) private __backing_unionA: (string | undefined) = ""; + + @JSONRename({newName:"classA"}) private __backing_classA: Person = new Person(); + + @JSONRename({newName:"enumA"}) private __backing_enumA: Status = Status.NotFound; + + public constructor() {} + + public get numA(): number { + 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"); + } + } + + public get stringA(): string { + 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"); + } + } + + public get booleanA(): boolean { + 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"); + } + } + + public get arrayA(): Array { + this.conditionalAddRef(this.__meta); + return this.__backing_arrayA; + } + + public set arrayA(newValue: Array) { + if (((this.__backing_arrayA) !== (newValue))) { + this.__backing_arrayA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("arrayA"); + } + } + + public get objectA(): Object { + this.conditionalAddRef(this.__meta); + return this.__backing_objectA; + } + + public set objectA(newValue: Object) { + if (((this.__backing_objectA) !== (newValue))) { + this.__backing_objectA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("objectA"); + } + } + + public get dateA(): Date { + this.conditionalAddRef(this.__meta); + return this.__backing_dateA; + } + + public set dateA(newValue: Date) { + if (((this.__backing_dateA) !== (newValue))) { + this.__backing_dateA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("dateA"); + } + } + + public get setA(): Set { + this.conditionalAddRef(this.__meta); + return this.__backing_setA; + } + + public set setA(newValue: Set) { + if (((this.__backing_setA) !== (newValue))) { + this.__backing_setA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("setA"); + } + } + + public get mapA(): Map { + this.conditionalAddRef(this.__meta); + return this.__backing_mapA; + } + + public set mapA(newValue: Map) { + if (((this.__backing_mapA) !== (newValue))) { + this.__backing_mapA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("mapA"); + } + } + + public get unionA(): (string | undefined) { + this.conditionalAddRef(this.__meta); + return this.__backing_unionA; + } + + public set unionA(newValue: (string | undefined)) { + if (((this.__backing_unionA) !== (newValue))) { + this.__backing_unionA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("unionA"); + } + } + + public get classA(): Person { + this.conditionalAddRef(this.__meta); + return this.__backing_classA; + } + + public set classA(newValue: Person) { + if (((this.__backing_classA) !== (newValue))) { + this.__backing_classA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("classA"); + } + } + + public get enumA(): Status { + this.conditionalAddRef(this.__meta); + return this.__backing_enumA; + } + + public set enumA(newValue: Status) { + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("enumA"); + } + } + +} + +class mixed3 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:"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 constructor() {} + + public get numA(): number { + 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"); + } + } + + public get stringA(): string { + 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"); + } + } + + public get booleanA(): boolean { + 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"); + } + } + + public get arrayA(): Array { + this.conditionalAddRef(this.__meta_arrayA); + return 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 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 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 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 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 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 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 this.__backing_enumA; + } + + public set enumA(newValue: Status) { + if (((this.__backing_enumA) !== (newValue))) { + this.__backing_enumA = newValue; + this.__meta_enumA.fireChange(); + this.executeOnSubscribingWatches("enumA"); + } + } + +} + +@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 {} + + @memo() public build() {} + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed track transform with complex type', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..8134669430d8b854efa7dfa934143d6d6b68130d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-extends.test.ts @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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-track-extends.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed track transform with extends', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + + + +@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(); + + @JSONRename({newName:"propA"}) private __backing_propA: number = 1; + + @JSONRename({newName:"trackA"}) private __backing_trackA: number = 2; + + public constructor() {} + + public get propA(): number { + 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"); + } + } + + public get trackA(): number { + 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"); + } + } + +} + +class G extends A { + public propG: number = 1; + + public constructor() {} + +} + +@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); + } + + 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:"propG"}) private __backing_propG: number = 1; + + @JSONStringifyIgnore() private __meta_propG: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get propG(): number { + 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"); + } + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed track transform with extends', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..2352837bbd6299b7b8f151d712d9e6c81cef0003 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-implements.test.ts @@ -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. + */ + +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-track-implements.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed track transform with implements', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Observed as Observed } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +interface PropInterface { + set propF(propF: number) + + get propF(): number + +} + +interface trackInterface { + set trackF(trackF: number) + + get trackF(): number + +} + +@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); + } + + 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:"propF"}) private __backing_propF: number = 1; + + @JSONRename({newName:"trackF"}) private __backing_trackF: number = 2; + + public constructor() {} + + set propF(newValue: number) { + if (((this.__backing_propF) !== (newValue))) { + this.__backing_propF = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("propF"); + } + } + + public get propF(): number { + this.conditionalAddRef(this.__meta); + return this.__backing_propF; + } + + set trackF(newValue: number) { + if (((this.__backing_trackF) !== (newValue))) { + this.__backing_trackF = newValue; + this.__meta.fireChange(); + this.executeOnSubscribingWatches("trackF"); + } + } + + public get trackF(): number { + this.conditionalAddRef(this.__meta); + return this.__backing_trackF; + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed track transform with implements', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..e82e823fc96ac5c12764516079689d761298c770 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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-track.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test observed with track transform', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + + + +@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(); + } + } + + public propB: number = 1; + + @JSONRename({newName:"trackB"}) private __backing_trackB: number = 2; + + @JSONStringifyIgnore() private __meta_trackB: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get trackB(): number { + 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"); + } + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test observed with track transform', + [observedTrackTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..a62942b8b560fff5f071f32d6181c1fd381531f2 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/track-only.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 'track-only.ets'), +]; + +const observedTrackTransform: Plugins = { + name: 'observedTrack', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test track only transform', 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Track as Track } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +class C 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 propC: number = 1; + + @JSONRename({newName:"trackC"}) private __backing_trackC: number = 2; + + @JSONStringifyIgnore() private __meta_trackC: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get trackC(): number { + 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"); + } + } + +} + +@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() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +function testObservedOnlyTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test track only transform', + [observedTrackTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testObservedOnlyTransformer], + }, + { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..048e585b9b6fe3522c74934a2cb7fe93abfc0f22 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Prop decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-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 { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = STATE_MGMT_FACTORY.makeProp(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); + 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 { + 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?: IPropDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + 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?: IPropDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + 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?: IPropDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Prop decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..68dc70cdef1a37603974f01469f8396b3897819d --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Prop decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-complex-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 { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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]))) { + 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.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); + 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 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); + 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 = STATE_MGMT_FACTORY.makeProp>(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); + 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 = 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 = 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 = 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 = 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 = STATE_MGMT_FACTORY.makeProp<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; + (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); + 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 { + 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?: IPropDecoratedVariable; + + public get propVar1(): Per { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: Per) { + this.__backing_propVar1!.set(value); + } + + 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?: IPropDecoratedVariable; + + public get propVar3(): PropType { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: PropType) { + this.__backing_propVar3!.set(value); + } + + 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?: IPropDecoratedVariable>; + + public get propVar5(): Array { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: Array) { + this.__backing_propVar5!.set(value); + } + + 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?: IPropDecoratedVariable>; + + public get propVar7(): Array { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: Array) { + this.__backing_propVar7!.set(value); + } + + 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?: IPropDecoratedVariable; + + public get propVar9(): Date { + return this.__backing_propVar9!.get(); + } + + public set propVar9(value: Date) { + this.__backing_propVar9!.set(value); + } + + 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?: IPropDecoratedVariable<(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?: IPropDecoratedVariable<(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() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Prop decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..620bd9884aedd66cff9d10617dc9117009a53c70 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.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 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-prop.ets'), +]; + +const pluginTester = new PluginTester('test @Prop decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'state-to-prop', + 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 { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 { + if (((({let gensym___188547633 = initializers; + (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { + this.__backing_count!.update((initializers!.count as number)); + } + } + + 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() { + Column(undefined, undefined, @memo() (() => { + if (((this.count) > (0))) { + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); + } else { + Text(undefined, "Game over!", undefined, undefined); + } + Button(@memo() ((instance: ButtonAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + })); + return; + }), "Try again", undefined, undefined); + })); + } + + private 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); + })); + } + + private constructor() {} + +} + +@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) + +} + +@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 @Prop decorated variables passing', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..6866b674f07bf06b805cc48e73b3a1287160c847 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-annotation-usage.test.ts @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'provide-annotation-usage.ets'), +]; + +const pluginTester = new PluginTester('test different @Provide annotation usage transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'provide-annotation-usage', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = 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 = 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 = 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 = 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 = 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 = STATE_MGMT_FACTORY.makeProvide<(string | undefined)>(this, "count6", "", ((({let gensym___37571585 = initializers; + (((gensym___37571585) == (null)) ? undefined : gensym___37571585.count6)})) ?? ("Child6")), true); + 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?: IProvideDecoratedVariable<(string | undefined)>; + + public get count(): (string | undefined) { + return this.__backing_count!.get(); + } + + public set count(value: (string | undefined)) { + this.__backing_count!.set(value); + } + + private __backing_count1?: IProvideDecoratedVariable<(string | undefined)>; + + public get count1(): (string | undefined) { + return this.__backing_count1!.get(); + } + + public set count1(value: (string | undefined)) { + this.__backing_count1!.set(value); + } + + private __backing_count2?: IProvideDecoratedVariable<(string | undefined)>; + + public get count2(): (string | undefined) { + return this.__backing_count2!.get(); + } + + public set count2(value: (string | undefined)) { + this.__backing_count2!.set(value); + } + + private __backing_count3?: IProvideDecoratedVariable<(string | undefined)>; + + public get count3(): (string | undefined) { + return this.__backing_count3!.get(); + } + + public set count3(value: (string | undefined)) { + this.__backing_count3!.set(value); + } + + private __backing_count4?: IProvideDecoratedVariable<(string | undefined)>; + + public get count4(): (string | undefined) { + return this.__backing_count4!.get(); + } + + public set count4(value: (string | undefined)) { + this.__backing_count4!.set(value); + } + + private __backing_count5?: IProvideDecoratedVariable<(string | undefined)>; + + public get count5(): (string | undefined) { + return this.__backing_count5!.get(); + } + + public set count5(value: (string | undefined)) { + this.__backing_count5!.set(value); + } + + private __backing_count6?: IProvideDecoratedVariable<(string | undefined)>; + + public get count6(): (string | undefined) { + return this.__backing_count6!.get(); + } + + public set count6(value: (string | undefined)) { + this.__backing_count6!.set(value); + } + + private __backing_count7?: IProvideDecoratedVariable<(string | undefined)>; + + public get count7(): (string | undefined) { + return this.__backing_count7!.get(); + } + + public set count7(value: (string | undefined)) { + this.__backing_count7!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test different @Provide annotation usage transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..9734b09b8dee6b35f1a501966470312b631fc7f4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-basic-type.test.ts @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'provide-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Provide decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'provide-basic-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar2", "provideVar2", ((({let gensym___143944235 = initializers; + (((gensym___143944235) == (null)) ? undefined : gensym___143944235.provideVar2)})) ?? (50)), false); + 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 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar4", "provideVar4", ((({let gensym___85711435 = initializers; + (((gensym___85711435) == (null)) ? undefined : gensym___85711435.provideVar4)})) ?? (undefined)), false); + 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?: IProvideDecoratedVariable; + + public get provideVar1(): string { + return this.__backing_provideVar1!.get(); + } + + public set provideVar1(value: string) { + this.__backing_provideVar1!.set(value); + } + + 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?: IProvideDecoratedVariable; + + public get provideVar3(): boolean { + return this.__backing_provideVar3!.get(); + } + + public set provideVar3(value: boolean) { + this.__backing_provideVar3!.set(value); + } + + 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?: IProvideDecoratedVariable; + + public get provideVar5(): null { + return this.__backing_provideVar5!.get(); + } + + public set provideVar5(value: null) { + this.__backing_provideVar5!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Provide decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..c7f6f689b2ed49bbc24d5353c950b42835e02155 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/provide-and-consume/provide-complex-type.test.ts @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/provide-and-consume'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'provide-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Provide decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'provide-complex-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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]))) { + 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_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 = 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 = STATE_MGMT_FACTORY.makeProvide(this, "provideVar3", "provideVar3", ((({let gensym___262195977 = initializers; + (((gensym___262195977) == (null)) ? undefined : gensym___262195977.provideVar3)})) ?? (PropType.TYPE3)), false); + 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 = STATE_MGMT_FACTORY.makeProvide>(this, "provideVar5", "provideVar5", ((({let gensym___139253630 = initializers; + (((gensym___139253630) == (null)) ? undefined : gensym___139253630.provideVar5)})) ?? ([true, false])), false); + 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 = 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 = 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 = 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 = 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 = 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 = 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?: IProvideDecoratedVariable; + + public get provideVar1(): Per { + return this.__backing_provideVar1!.get(); + } + + public set provideVar1(value: Per) { + this.__backing_provideVar1!.set(value); + } + + 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?: IProvideDecoratedVariable; + + public get provideVar3(): PropType { + return this.__backing_provideVar3!.get(); + } + + public set provideVar3(value: PropType) { + this.__backing_provideVar3!.set(value); + } + + 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?: IProvideDecoratedVariable>; + + public get provideVar5(): Array { + return this.__backing_provideVar5!.get(); + } + + public set provideVar5(value: Array) { + this.__backing_provideVar5!.set(value); + } + + 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?: IProvideDecoratedVariable>; + + public get provideVar7(): Array { + return this.__backing_provideVar7!.get(); + } + + public set provideVar7(value: Array) { + this.__backing_provideVar7!.set(value); + } + + 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?: IProvideDecoratedVariable; + + public get provideVar9(): Date { + return this.__backing_provideVar9!.get(); + } + + public set provideVar9(value: Date) { + this.__backing_provideVar9!.set(value); + } + + 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?: IProvideDecoratedVariable<(string | number)>; + + public get provideVar11(): (string | number) { + return this.__backing_provideVar11!.get(); + } + + public set provideVar11(value: (string | number)) { + this.__backing_provideVar11!.set(value); + } + + private __backing_provideVar12?: IProvideDecoratedVariable<(Set | Per)>; + + public get provideVar12(): (Set | Per) { + return this.__backing_provideVar12!.get(); + } + + public set provideVar12(value: (Set | Per)) { + this.__backing_provideVar12!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Provide decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..643f1955b746eb9274a35b2db1f17e6ed9f6740c --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts @@ -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. + */ + +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/resource'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'resource-in-build.ets'), +]; + +const pluginTester = new PluginTester('test resource transform in build method', buildConfig); + +const parsedTransform: Plugins = { + name: 'resource-in-build', + parsed: uiTransform().parsed, +}; + +const expectedScript: string = ` +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 { ImageAttribute as ImageAttribute } from "arkui.component.image"; + +import { _r as _r } from "arkui.component.resources"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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, Resource as Resource } from "@ohos.arkui.component"; + +function main() {} + +@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 {} + + 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() { + 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(16777220, 10003, "com.example.mock", "entry"), + }, 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(16777223, 20000, "com.example.mock", "entry"), + }, { + value: "bbb", + icon: _r(16777223, 20000, "com.example.mock", "entry"), + }, { + value: "ccc", + icon: _r(16777223, 20000, "com.example.mock", "entry"), + }, { + value: "ddd", + icon: _r(16777223, 20000, "com.example.mock", "entry"), + }), undefined); + Image(@memo() ((instance: ImageAttribute): void => { + instance.margin(({ + top: _r(16777222, 10002, "com.example.mock", "entry"), + bottom: _r(16777222, 10002, "com.example.mock", "entry"), + } as Margin)); + return; + }), _r(16777217, 20000, "com.example.mock", "entry"), undefined, undefined); + ImageAnimator(@memo() ((instance: ImageAnimatorAttribute): void => { + instance.images([{ + src: _r(16777217, 20000, "com.example.mock", "entry"), + }, { + src: _r(16777225, 20000, "com.example.mock", "entry"), + }]); + return; + })); + })); + } + + private constructor() {} + +} + +@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) +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test resource transform in build method', + [parsedTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..89b0c4750721f5b34a7d114f845f3b4728471aaa --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/resource'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'resource-in-property.ets'), +]; + +const pluginTester = new PluginTester('test resource transform in property', buildConfig); + +const parsedTransform: Plugins = { + name: 'resource-in-property', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { _rawfile as _rawfile } from "arkui.component.resources"; +import { _r as _r } from "arkui.component.resources"; +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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(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(16777216, 10003, "com.example.mock", "entry"))); + this.__backing_icon = ((({let gensym___38135554 = initializers; + (((gensym___38135554) == (null)) ? undefined : gensym___38135554.icon)})) ?? (_rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"))); + } + public __updateStruct(initializers: (__Options_ResourceComponent | undefined)): void {} + private __backing_str?: Resource; + public get str(): Resource { + return (this.__backing_str as Resource); + } + public set str(value: Resource) { + this.__backing_str = value; + } + private __backing_icon?: Resource; + public get icon(): Resource { + return (this.__backing_icon as Resource); + } + public set icon(value: Resource) { + this.__backing_icon = value; + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Text(undefined, this.str, undefined, undefined); + Text(undefined, i, undefined, undefined); + Image(undefined, this.icon, undefined, undefined); + })); + } + private constructor() {} + +} + +@Component() export interface __Options_ResourceComponent { + set str(str: (Resource | undefined)) + get str(): (Resource | undefined) + set icon(icon: (Resource | undefined)) + get icon(): (Resource | undefined) +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test resource transform in property', + [parsedTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..004ae0a02899cb64f8104f34f78e19f47c5e9074 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.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 REUSABLE_DIR_PATH: string = 'decorators/reusable'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, REUSABLE_DIR_PATH, 'reusable-basic.ets'), +]; + +const reusableTransform: Plugins = { + name: 'reusable', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test basic reusable', buildConfig); + +const expectedScript: string = ` +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 { memo as memo } from "arkui.stateManagement.runtime"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Reusable as Reusable } from "@ohos.arkui.component"; + +import { State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@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() { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + num: 5, + }, "Child", undefined); + } + + private constructor() {} + +} + +@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 { + if (((({let gensym___108716469 = initializers; + (((gensym___108716469) == (null)) ? undefined : gensym___108716469.num)})) !== (undefined))) { + this.__backing_num!.update((initializers!.num as number)); + } + } + + public override constructor __toRecord(params: Object): Record { + const paramsCasted = (params as __Options_Child); + return { + "num": ((paramsCasted.num) ?? (new Object())), + "num1": ((paramsCasted.num1) ?? (new Object())), + }; + } + + private __backing_num?: IPropDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + + private __backing_num1?: IStateDecoratedVariable; + + public get num1(): number { + return this.__backing_num1!.get(); + } + + public set num1(value: number) { + this.__backing_num1!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} + +@Component() @Reusable() export interface __Options_Child { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IPropDecoratedVariable | undefined)) + + get __backing_num(): (IPropDecoratedVariable | undefined) + set num1(num1: (number | undefined)) + + get num1(): (number | undefined) + set __backing_num1(__backing_num1: (IStateDecoratedVariable | undefined)) + + get __backing_num1(): (IStateDecoratedVariable | undefined) + +} +`; + +function testReusableTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic reusable', + [reusableTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..e3f98d4159c59c420350875c8627589b1ca50cef --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 REUSABLE_DIR_PATH: string = 'decorators/reusable'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, REUSABLE_DIR_PATH, 'reusable-complex.ets'), +]; + +const reusableTransform: Plugins = { + name: 'reusable', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test complex reusable', buildConfig); + +const expectedScript: string = ` + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +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 { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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"; + +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 constructor(value: string) { + this.value = value; + } + +} + +@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 {} + + private __backing_display?: IStateDecoratedVariable; + + public get display(): boolean { + return this.__backing_display!.get(); + } + + public set display(value: boolean) { + this.__backing_display!.set(value); + } + + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { + instance.height("100%").width("100%"); + return; + }), 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"), + }, "Child", undefined); + } + })); + } + + private constructor() {} + +} + +@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 override constructor __toRecord(params: Object): Record { + const paramsCasted = (params as __Options_Child); + return { + "message": ((paramsCasted.message) ?? (new Object())), + }; + } + + private __backing_message?: IStateDecoratedVariable; + + public get message(): Message { + return this.__backing_message!.get(); + } + + public set message(value: Message) { + this.__backing_message!.set(value); + } + + public aboutToReuse(params: Record) { + console.info("Recycle ====Child=="); + } + + @memo() public build() { + Column(@memo() ((instance: ColumnAttribute): void => { + instance.borderWidth(1).height(100); + return; + }), undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(30); + return; + }), this.message.value, undefined, undefined); + })); + } + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set display(display: (boolean | undefined)) + + get display(): (boolean | undefined) + set __backing_display(__backing_display: (IStateDecoratedVariable | undefined)) + + get __backing_display(): (IStateDecoratedVariable | undefined) + +} + +@Reusable() @Component() export interface __Options_Child { + set message(message: (Message | undefined)) + + get message(): (Message | 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 testReusableTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex reusable', + [reusableTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..c47bd027b7b07e0346803f7752b3ae88a41393f6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.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 } 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/state'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + 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); + +const parsedTransform: Plugins = { + name: 'state-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 { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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 = STATE_MGMT_FACTORY.makeState(this, "stateVar2", ((({let gensym___113574154 = initializers; + (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (50))); + this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; + (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (true))); + this.__backing_stateVar4 = STATE_MGMT_FACTORY.makeState(this, "stateVar4", ((({let gensym___148024261 = initializers; + (((gensym___148024261) == (null)) ? undefined : gensym___148024261.stateVar4)})) ?? (undefined))); + 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?: IStateDecoratedVariable; + + public get stateVar1(): string { + return this.__backing_stateVar1!.get(); + } + + public set stateVar1(value: string) { + this.__backing_stateVar1!.set(value); + } + + 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?: IStateDecoratedVariable; + + public get stateVar3(): boolean { + return this.__backing_stateVar3!.get(); + } + + public set stateVar3(value: boolean) { + this.__backing_stateVar3!.set(value); + } + + 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?: IStateDecoratedVariable; + + public get stateVar5(): null { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: null) { + this.__backing_stateVar5!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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 { + console.log("[testParsedAndCheckedTransformer] this: ", this); + console.log("[testParsedAndCheckedTransformer] parseDumpSrc(this.scriptSnapshot ?? ''): ", parseDumpSrc(this.scriptSnapshot ?? '')); + console.log("[testParsedAndCheckedTransformer] parseDumpSrc(expectedScript): ", parseDumpSrc(expectedScript)); + + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @State decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..cdd84ec17c02276bfadc182b91cd2724b9416b9f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-complex-type.test.ts @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/state'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + 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); + +const parsedTransform: Plugins = { + name: 'state-complex-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 { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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]))) { + 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(); + } + +} + +@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 = 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 = STATE_MGMT_FACTORY.makeState(this, "stateVar3", ((({let gensym___166994972 = initializers; + (((gensym___166994972) == (null)) ? undefined : gensym___166994972.stateVar3)})) ?? (StateType.TYPE3))); + 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 = STATE_MGMT_FACTORY.makeState>(this, "stateVar5", ((({let gensym___99384342 = initializers; + (((gensym___99384342) == (null)) ? undefined : gensym___99384342.stateVar5)})) ?? ([true, false]))); + 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 = 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 = 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 = 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 = 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 = STATE_MGMT_FACTORY.makeState<(string | number)>(this, "stateVar11", ((({let gensym___56045278 = initializers; + (((gensym___56045278) == (null)) ? undefined : gensym___56045278.stateVar11)})) ?? (0.0))); + 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?: IStateDecoratedVariable; + + public get stateVar1(): Per { + return this.__backing_stateVar1!.get(); + } + + public set stateVar1(value: Per) { + this.__backing_stateVar1!.set(value); + } + + 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?: IStateDecoratedVariable; + + public get stateVar3(): StateType { + return this.__backing_stateVar3!.get(); + } + + public set stateVar3(value: StateType) { + this.__backing_stateVar3!.set(value); + } + + 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?: IStateDecoratedVariable>; + + public get stateVar5(): Array { + return this.__backing_stateVar5!.get(); + } + + public set stateVar5(value: Array) { + this.__backing_stateVar5!.set(value); + } + + 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?: IStateDecoratedVariable>; + + public get stateVar7(): Array { + return this.__backing_stateVar7!.get(); + } + + public set stateVar7(value: Array) { + this.__backing_stateVar7!.set(value); + } + + 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?: IStateDecoratedVariable; + + public get stateVar9(): Date { + return this.__backing_stateVar9!.get(); + } + + public set stateVar9(value: Date) { + this.__backing_stateVar9!.set(value); + } + + 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?: IStateDecoratedVariable<(string | number)>; + + public get stateVar11(): (string | number) { + return this.__backing_stateVar11!.get(); + } + + public set stateVar11(value: (string | number)) { + this.__backing_stateVar11!.set(value); + } + + private __backing_stateVar12?: IStateDecoratedVariable<(Set | Per)>; + + public get stateVar12(): (Set | Per) { + return this.__backing_stateVar12!.get(); + } + + public set stateVar12(value: (Set | Per)) { + this.__backing_stateVar12!.set(value); + } + + @memo() public build() {} + + private constructor() {} + +} + +@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 { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @State decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..b470b7a1068dd37d4298a312f3f04ae11dd9263a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.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 STATE_DIR_PATH: string = 'decorators/state'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STATE_DIR_PATH, 'state-to-state.ets'), +]; + +const pluginTester = new PluginTester('test @State decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'state-to-state', + 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() 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?: IStateDecoratedVariable; + + public get parentVar1(): Per { + return this.__backing_parentVar1!.get(); + } + + public set parentVar1(value: Per) { + this.__backing_parentVar1!.set(value); + } + + @memo() public build() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), { + childVar1: this.parentVar1, + }, undefined, undefined); + })); + } + + private constructor() {} + +} + +@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?: IStateDecoratedVariable; + + public get childVar1(): Per { + return this.__backing_childVar1!.get(); + } + + public set childVar1(value: Per) { + this.__backing_childVar1!.set(value); + } + + @memo() public build() { + Text(undefined, this.childVar1.str, undefined, undefined); + } + + private constructor() {} + +} + +@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) + +} + +@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 { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @State decorated variables passing', + [parsedTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..b6d7d10b2ed230be50ef4305501e1c4c0b77d78e --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.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 STORAGELINK_DIR_PATH: string = 'decorators/storagelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGELINK_DIR_PATH, 'storagelink-appstorage.ets'), +]; + +const storageLinkTransform: Plugins = { + name: 'storageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storagelink with appstorage', buildConfig); + +const expectedScript: string = ` +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 { 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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() {} + +AppStorage.setOrCreate("PropA", 47, Type.from()); +AppStorage.setOrCreate("PropB", new Data(50), Type.from()); + +__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() 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, Type.from()) + this.__backing_storageLinkObject = STATE_MGMT_FACTORY.makeStorageLink(this, "PropB", "storageLinkObject", new Data(1), Type.from()) + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_storageLink?: IStorageLinkDecoratedVariable; + + public get storageLink(): number { + return this.__backing_storageLink!.get(); + } + + public set storageLink(value: number) { + this.__backing_storageLink!.set(value); + } + + private __backing_storageLinkObject?: IStorageLinkDecoratedVariable; + + public get storageLinkObject(): Data { + return this.__backing_storageLinkObject!.get(); + } + + public set storageLinkObject(value: Data) { + this.__backing_storageLinkObject!.set(value); + } + + @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: TextAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.storageLinkObject.code += 1; + })); + return; + }), \`From AppStorage \${this.storageLinkObject.code}\`, undefined, undefined); + })); + } + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageLink(storageLink: (number | undefined)) + + get storageLink(): (number | undefined) + set __backing_storageLink(__backing_storageLink: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_storageLink(): (IStorageLinkDecoratedVariable | undefined) + set storageLinkObject(storageLinkObject: (Data | undefined)) + + get storageLinkObject(): (Data | undefined) + set __backing_storageLinkObject(__backing_storageLinkObject: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_storageLinkObject(): (IStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storagelink with appstorage', + [storageLinkTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..45b0eed2b1ccb9633469506eb853228b03dca2cf --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 STORAGELINK_DIR_PATH: string = 'decorators/storagelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGELINK_DIR_PATH, 'storagelink-complex-type.ets'), +]; + +const storageLinkTransform: Plugins = { + name: 'storageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storagelink 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 { 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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 = ""; + + 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.makeStorageLink>(this, "Prop1", "arrayA", [1, 2, 3], Type.from>()) + this.__backing_objectA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "objectA", {}, Type.from()) + this.__backing_dateA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "dateA", new Date("2021-08-08"), Type.from()) + this.__backing_setA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop4", "setA", new Set(), Type.from>()) + this.__backing_mapA = STATE_MGMT_FACTORY.makeStorageLink>(this, "Prop5", "mapA", new Map(), Type.from>()) + this.__backing_classA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop7", "classA", new Person("John"), Type.from()) + this.__backing_enumA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop8", "enumA", Status.NotFound, Type.from()) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayA?: IStorageLinkDecoratedVariable>; + + public get arrayA(): Array { + return this.__backing_arrayA!.get(); + } + + public set arrayA(value: Array) { + this.__backing_arrayA!.set(value); + } + + private __backing_objectA?: IStorageLinkDecoratedVariable; + + public get objectA(): Object { + return this.__backing_objectA!.get(); + } + + public set objectA(value: Object) { + this.__backing_objectA!.set(value); + } + + private __backing_dateA?: IStorageLinkDecoratedVariable; + + public get dateA(): Date { + return this.__backing_dateA!.get(); + } + + public set dateA(value: Date) { + this.__backing_dateA!.set(value); + } + + private __backing_setA?: IStorageLinkDecoratedVariable>; + + public get setA(): Set { + return this.__backing_setA!.get(); + } + + public set setA(value: Set) { + this.__backing_setA!.set(value); + } + + private __backing_mapA?: IStorageLinkDecoratedVariable>; + + public get mapA(): Map { + return this.__backing_mapA!.get(); + } + + public set mapA(value: Map) { + this.__backing_mapA!.set(value); + } + + private __backing_classA?: IStorageLinkDecoratedVariable; + + public get classA(): Person { + return this.__backing_classA!.get(); + } + + public set classA(value: Person) { + this.__backing_classA!.set(value); + } + + private __backing_enumA?: IStorageLinkDecoratedVariable; + + public get enumA(): Status { + return this.__backing_enumA!.get(); + } + + public set enumA(value: Status) { + this.__backing_enumA!.set(value); + } + + @memo() public build() {} + + private 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: (IStorageLinkDecoratedVariable> | undefined)) + + get __backing_arrayA(): (IStorageLinkDecoratedVariable> | undefined) + set objectA(objectA: (Object | undefined)) + + get objectA(): (Object | undefined) + set __backing_objectA(__backing_objectA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_objectA(): (IStorageLinkDecoratedVariable | undefined) + set dateA(dateA: (Date | undefined)) + + get dateA(): (Date | undefined) + set __backing_dateA(__backing_dateA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_dateA(): (IStorageLinkDecoratedVariable | undefined) + set setA(setA: (Set | undefined)) + + get setA(): (Set | undefined) + set __backing_setA(__backing_setA: (IStorageLinkDecoratedVariable> | undefined)) + + get __backing_setA(): (IStorageLinkDecoratedVariable> | undefined) + set mapA(mapA: (Map | undefined)) + + get mapA(): (Map | undefined) + set __backing_mapA(__backing_mapA: (IStorageLinkDecoratedVariable> | undefined)) + + get __backing_mapA(): (IStorageLinkDecoratedVariable> | undefined) + set classA(classA: (Person | undefined)) + + get classA(): (Person | undefined) + set __backing_classA(__backing_classA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_classA(): (IStorageLinkDecoratedVariable | undefined) + set enumA(enumA: (Status | undefined)) + + get enumA(): (Status | undefined) + set __backing_enumA(__backing_enumA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_enumA(): (IStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storagelink complex type transform', + [storageLinkTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..057ffec8607656cae6bae369f54ab48e693135e6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.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 } 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 STORAGELINK_DIR_PATH: string = 'decorators/storagelink'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGELINK_DIR_PATH, 'storagelink-primitive-type.ets'), +]; + +const storageLinkTransform: Plugins = { + name: 'storageLink', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storagelink 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 { 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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-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, Type.from()) + this.__backing_stringA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop2", "stringA", "AA", Type.from()) + this.__backing_booleanA = STATE_MGMT_FACTORY.makeStorageLink(this, "Prop3", "booleanA", true, Type.from()) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numA?: IStorageLinkDecoratedVariable; + + public get numA(): number { + return this.__backing_numA!.get(); + } + + public set numA(value: number) { + this.__backing_numA!.set(value); + } + + private __backing_stringA?: IStorageLinkDecoratedVariable; + + public get stringA(): string { + return this.__backing_stringA!.get(); + } + + public set stringA(value: string) { + this.__backing_stringA!.set(value); + } + + private __backing_booleanA?: IStorageLinkDecoratedVariable; + + public get booleanA(): boolean { + return this.__backing_booleanA!.get(); + } + + public set booleanA(value: boolean) { + this.__backing_booleanA!.set(value); + } + + @memo() public build() {} + + private 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: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_numA(): (IStorageLinkDecoratedVariable | undefined) + set stringA(stringA: (string | undefined)) + + get stringA(): (string | undefined) + set __backing_stringA(__backing_stringA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_stringA(): (IStorageLinkDecoratedVariable | undefined) + set booleanA(booleanA: (boolean | undefined)) + + get booleanA(): (boolean | undefined) + set __backing_booleanA(__backing_booleanA: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_booleanA(): (IStorageLinkDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStorageLinkTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storagelink primitive type transform', + [storageLinkTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStorageLinkTransformer], + }, + { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..cbd0b0844779a8d64073f95dc1899f5b045a754a --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.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 { 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-appstorage.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop with appstorage', buildConfig); + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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 { StorageProp as StorageProp, AppStorage as AppStorage } from "@ohos.arkui.stateManagement"; + +function main() {} + +AppStorage.setOrCreate("PropA", 47, Type.from()); +AppStorage.setOrCreate("PropB", new Data(50), Type.from()); + +__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; + + public constructor(code: number) { + this.code = code; + } + +} + +@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, Type.from()) + this.__backing_storagePropObject = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropB", "storagePropObject", new Data(1), Type.from()) + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_storageProp?: IStoragePropRefDecoratedVariable; + + public get storageProp(): number { + return this.__backing_storageProp!.get(); + } + + public set storageProp(value: number) { + this.__backing_storageProp!.set(value); + } + + private __backing_storagePropObject?: IStoragePropRefDecoratedVariable; + + public get storagePropObject(): Data { + return this.__backing_storagePropObject!.get(); + } + + public set storagePropObject(value: Data) { + this.__backing_storagePropObject!.set(value); + } + + @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: TextAttribute): void => { + instance.onClick(((e: ClickEvent) => { + this.storagePropObject.code += 1; + })); + return; + }), \`From AppStorage \${this.storagePropObject.code}\`, undefined, undefined); + })); + } + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageProp(storageProp: (number | undefined)) + + get storageProp(): (number | undefined) + set __backing_storageProp(__backing_storageProp: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_storageProp(): (IStoragePropRefDecoratedVariable | undefined) + set storagePropObject(storagePropObject: (Data | undefined)) + + get storagePropObject(): (Data | undefined) + set __backing_storagePropObject(__backing_storagePropObject: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_storagePropObject(): (IStoragePropRefDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop with appstorage', + [storagePropTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..c5d5cf897271ad9d5c656f42b857dc2f75e9a8d7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop 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 { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; +import { EntryPoint as EntryPoint } from "arkui.UserView"; +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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 = ""; + + 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_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3], Type.from>()) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}, Type.from()) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09"), Type.from()) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set(), Type.from>()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map(), Type.from>()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin"), Type.from()) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound, Type.from()) + } + + 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() {} + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @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) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..03d383f07733f2d23d9b902080fcacbfefab8b58 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.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 STORAGEPROP_DIR_PATH: string = 'decorators/storageprop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop 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 { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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-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, Type.from()) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB", Type.from()) + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false, Type.from()) + } + + 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); + } + + @memo() public build() {} + + private constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @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) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + '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 new file mode 100644 index 0000000000000000000000000000000000000000..9857c430b120911c767beab00edac722431433c1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 WATCH_DIR_PATH: string = 'decorators/watch'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, WATCH_DIR_PATH, 'watch-basic.ets'), +]; + +const watchTransform: Plugins = { + name: 'watch', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test basic watch transform', buildConfig); + +const expectedScript: string = ` +import { IConsumeDecoratedVariable as IConsumeDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; + +import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +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 { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + +import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +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, 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 propA: string = "hello"; + + @JSONRename({newName:"trackA"}) private __backing_trackA: string = "world"; + + @JSONStringifyIgnore() private __meta_trackA: IMutableStateMeta = STATE_MGMT_FACTORY.makeMutableStateMeta(); + + public constructor() {} + + public get trackA(): string { + 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"); + } + } + +} + +@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 = STATE_MGMT_FACTORY.makeProp(this, "propvar", ((({let gensym___241486692 = initializers; + (((gensym___241486692) == (null)) ? undefined : gensym___241486692.propvar)})) ?? ("Hello World")), ((_: string): void => { + this.propOnChange(_); + })); + 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", Type.from(), ((_: string): void => { + this.storageLinkOnChange(_); + })) + this.__backing_storagepropvar = STATE_MGMT_FACTORY.makeStoragePropRef(this, "prop2", "storagepropvar", "Hello World", Type.from(), ((_: string): void => { + this.storagePropOnChange(_); + })) + this.__backing_objectlinkvar = STATE_MGMT_FACTORY.makeObjectLink(this, "objectlinkvar", ({let gensym___172556967 = initializers; + (((gensym___172556967) == (null)) ? undefined : gensym___172556967.objectlinkvar)})!, ((_: 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 { + 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?: IStateDecoratedVariable; + + public get statevar(): string { + return this.__backing_statevar!.get(); + } + + public set statevar(value: string) { + this.__backing_statevar!.set(value); + } + + private __backing_propvar?: IPropDecoratedVariable; + + public get propvar(): string { + return this.__backing_propvar!.get(); + } + + public set propvar(value: string) { + this.__backing_propvar!.set(value); + } + + 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(); + } + + public set storagelinkvar(value: string) { + this.__backing_storagelinkvar!.set(value); + } + + private __backing_storagepropvar?: IStoragePropRefDecoratedVariable; + + public get storagepropvar(): string { + return this.__backing_storagepropvar!.get(); + } + + public set storagepropvar(value: string) { + this.__backing_storagepropvar!.set(value); + } + + 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(); + } + + public set providevar(value: string) { + this.__backing_providevar!.set(value); + } + + public stateOnChange(propName: string) {} + + 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() { + Column(undefined, undefined, @memo() (() => { + Child._instantiateImpl(undefined, (() => { + return new Child(); + }), undefined, undefined, undefined); + })); + } + + private constructor() {} + +} + +@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 ConsumeOnChange(propName: string) {} + + @memo() public build() {} + + private constructor() {} + +} + +@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) + set __backing_statevar(__backing_statevar: (IStateDecoratedVariable | undefined)) + + get __backing_statevar(): (IStateDecoratedVariable | undefined) + set propvar(propvar: (string | undefined)) + + get propvar(): (string | undefined) + set __backing_propvar(__backing_propvar: (IPropDecoratedVariable | undefined)) + + get __backing_propvar(): (IPropDecoratedVariable | undefined) + @__Link_intrinsic() set linkvar(linkvar: (string | undefined)) + + @__Link_intrinsic() get linkvar(): (string | undefined) + set __backing_linkvar(__backing_linkvar: (LinkSourceType | undefined)) + + get __backing_linkvar(): (LinkSourceType | undefined) + set storagelinkvar(storagelinkvar: (string | undefined)) + + get storagelinkvar(): (string | undefined) + set __backing_storagelinkvar(__backing_storagelinkvar: (IStorageLinkDecoratedVariable | undefined)) + + get __backing_storagelinkvar(): (IStorageLinkDecoratedVariable | undefined) + set storagepropvar(storagepropvar: (string | undefined)) + + get storagepropvar(): (string | undefined) + set __backing_storagepropvar(__backing_storagepropvar: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_storagepropvar(): (IStoragePropRefDecoratedVariable | undefined) + set objectlinkvar(objectlinkvar: (A | undefined)) + + get objectlinkvar(): (A | undefined) + set __backing_objectlinkvar(__backing_objectlinkvar: (IObjectLinkDecoratedVariable | undefined)) + + get __backing_objectlinkvar(): (IObjectLinkDecoratedVariable | undefined) + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + set __backing_providevar(__backing_providevar: (IProvideDecoratedVariable | undefined)) + + get __backing_providevar(): (IProvideDecoratedVariable | undefined) + +} + +@Component() export interface __Options_Child { + set providevar(providevar: (string | undefined)) + + get providevar(): (string | undefined) + set __backing_providevar(__backing_providevar: (IConsumeDecoratedVariable | undefined)) + + get __backing_providevar(): (IConsumeDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testWatchTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic watch transform', + [watchTransform, uiNoRecheck, recheck], + { + '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..74d90e24922360f2d60d18824b28ddceb37994a4 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); + })); + })); + })); + } + + private 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/entry/entry-only.test.ts b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..2545064bca000b9e8036cd0b3f7f6bb7fd6961a1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/entry-only.test.ts @@ -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 * 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..23b0c1164a265525f71d177c5a9f011e952f3723 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-false.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..7f312d799738ad4d1d8a90f14bac0b3ba7c10042 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage-use-shared-storage-true.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..6c62d8b25e481b2a7c6aa6aa9245e097cda736dc --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/storage.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..76cd2c907446f944cfa7046add3f65fd3efa9d2f --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-false.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..363159cc64cb879b38ae54e6d4ffd33d965b3814 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/localstorage/use-shared-storage-true.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..367564302df027ae0a198301fa8a3c751558f833 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name-storage-shared.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..466feecd263b23b93d835d8ad318d093f518f992 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/entry/route-name/route-name.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/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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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..dc71b2bd38dd8f8738bd020bc1aa912cc1c03ab1 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.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 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); + })); + } + + private 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..53bb01be8d98fcdb61429fae034b76a6f55881e0 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); + })); + } + + private 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/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..b88e465bd50c76f361cacdb49b8baa89fc825673 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.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 { 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; +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() {} + +globalBuilder = wrapBuilder(myBuilder); +@memo() function myBuilder(value: string, size: number) { + Text(@memo() ((instance: TextAttribute): void => { + instance.fontSize(size); + return; + }), value, undefined, undefined); +} + + +@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); + })); + } + + private 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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() {} + +globalBuilder = wrapBuilder(myBuilder); +@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() 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; + } + } + + private 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-ui.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..42bb161d17100424a3e0dbfca01bd02e8ac88537 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.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 { 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); +} + + +@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(); + })); + } + + private 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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; + } +} + + +@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; + } + } + + private 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..caa6e8154b579a1a1d1551c133bb8b15b241d591 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-with-lambda.test.ts @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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); + })); +} + + +@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); + })); + } + + private 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 { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + +import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + +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; + } +} + + +@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; + } + } + + private 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 db63f0e4b5157303368cb2ede8c1d7d7a0d328c1..d47091bd71dcb491d82c04c6a2e48e7e4f282869 100644 --- a/arkui-plugins/test/utils/artkts-config.ts +++ b/arkui-plugins/test/utils/artkts-config.ts @@ -21,20 +21,27 @@ import { changeFileExtension, ensurePathExists, getFileName, - getPandaSDKPath, + 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: { @@ -46,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; @@ -66,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; @@ -113,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( @@ -159,17 +133,25 @@ function getDependentModules(moduleInfo: ModuleInfo, moduleInfoMap: Map) { +function traverseSDK(currentDir: string, pathSection: Record, prefix?: string) { const items = fs.readdirSync(currentDir); + for (const item of items) { const itemPath = path.join(currentDir, item); const stat = fs.statSync(itemPath); - if (stat.isFile()) { + if (stat.isFile() && !itemPath.endsWith('.d.ets')) { + continue; + } + + if (stat.isFile() && itemPath.endsWith('.d.ets')) { const basename = path.basename(item, '.d.ets'); - pathSection[basename] = [changeFileExtension(itemPath, '', '.d.ets')]; + const name = prefix && prefix !== 'arkui.runtime-api' ? `${prefix}.${basename}` : basename; + pathSection[name] = [changeFileExtension(itemPath, '', '.d.ets')]; } else if (stat.isDirectory()) { - traverse(itemPath, pathSection); + const basename = path.basename(itemPath); + const name = prefix && prefix !== 'arkui.runtime-api' ? `${prefix}.${basename}` : basename; + traverseSDK(itemPath, pathSection, name); } } } @@ -180,57 +162,132 @@ function mockBuildConfig(): BuildConfig { compileFiles: [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, MOCK_ENTRY_FILE_NAME)], loaderOutPath: path.resolve(getRootPath(), MOCK_OUTPUT_DIR_PATH), cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), - pandaSdkPath: getPandaSDKPath(), - buildSdkPath: path.resolve(getRootPath(), MOCK_LOCAL_SDK_DIR_PATH), + pandaSdkPath: global.PANDA_SDK_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: [ - { - packageName: '@koalaui/runtime', - moduleName: '@koalaui/runtime', - moduleType: 'har', - modulePath: path.resolve(getRootPath(), RUNTIME_API_PATH), - sourceRoots: ['./'], - entryFile: 'index.sts', - }, - ], + 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 { @@ -243,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); @@ -257,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); }); } @@ -294,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)]; - traverse(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])]; @@ -316,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 new file mode 100644 index 0000000000000000000000000000000000000000..06942d1c4635afeecd940407dcfeabf77ca12db7 --- /dev/null +++ b/arkui-plugins/test/utils/cache.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 { ArkTSConfigContext, FileDependencyContext, PluginTestContext } from './shared-types'; + +class TesterCache { + private cacheInfo: Map; + + constructor() { + this.cacheInfo = new Map(); + } + + public delete(key: string) { + if (this.cacheInfo.has(key)) { + this.cacheInfo.delete(key); + } + } + + public get(key: string) { + if (this.cacheInfo.has(key)) { + return this.cacheInfo.get(key); + } + return undefined; + } + + public has(key: string) { + return this.cacheInfo.has(key); + } + + public set(key: string, value: T) { + if (!this.cacheInfo.has(key)) { + this.cacheInfo.set(key, value); + } + } + + public clear() { + this.cacheInfo.clear(); + } +} + +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 7694eac9f9e651bae52e43fc548d8854cbb1cd5c..afa55219ab9435046acb914644789ece536583c9 100644 --- a/arkui-plugins/test/utils/compile.ts +++ b/arkui-plugins/test/utils/compile.ts @@ -14,50 +14,286 @@ */ import * as arkts from '@koalaui/libarkts'; -import { arktsGlobal } from '@koalaui/libarkts/build/lib/es2panda'; +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 { PluginContext, PluginExecutor } from '../../common/plugin-context'; -import { EtsglobalRemover } from '../../common/etsglobal-remover'; +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; +} - if (restart) { - const srcText = new EtsglobalRemover().visitor(ast).dumpSrc(); - arktsGlobal.es2panda._DestroyContext(arktsGlobal.context); - arktsGlobal.compilerContext = arkts.Context.createFromString(srcText); +function collectPluginTextContextFromSourceProgram(program: arkts.Program, tracing: TraceOptions): PluginTestContext { + const pluginTestContext: PluginTestContext = {}; + const script: arkts.EtsScript = program.astNode; + pluginTestContext.scriptSnapshot = script.dumpSrc(); + return pluginTestContext; +} + +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) { - pluginContext.setArkTSAst(ast); - pluginContext.setArkTSProgram(arktsGlobal.compilerContext.program); - 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 4dc3d85110793f1ac96ebdc88cf65cd14d7adb9d..424d4512edbc6ec0b3f3cdef7ce8ab215b795b65 100644 --- a/arkui-plugins/test/utils/global.ts +++ b/arkui-plugins/test/utils/global.ts @@ -13,16 +13,19 @@ * limitations under the License. */ -import * as fs from 'fs'; +import fs from 'fs' import * as arkts from '@koalaui/libarkts'; -import { arktsGlobal } from '@koalaui/libarkts/build/lib/es2panda'; -import { CompileFileInfo } from './artkts-config'; +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', - 'sts', + 'ets', '--arktsconfig', fileInfo.arktsConfigFile, '--output', @@ -32,59 +35,100 @@ function initGlobal(fileInfo: CompileFileInfo, isDebug: boolean = true): void { if (isDebug) { config.push('--debug-info'); } + if (!isUseCache) { + config.push('--simultaneous'); + } config.push(fileInfo.filePath); - arktsGlobal.filePath = fileInfo.filePath; - resetConfig(config); + if (isUseCache) { + arkts.MemInitialize(); + } + arkts.arktsGlobal.filePath = fileInfo.filePath; + 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); +} + +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); +} - const source: string = fs.readFileSync(fileInfo.filePath).toString(); - resetContext(source); +function createContextGenerateAbcForExternalSourceFiles(filePaths: string[]): arkts.Context { + return arkts.Context.createContextGenerateAbcForExternalSourceFiles(filePaths); } function resetContext(source: string): void { try { - arktsGlobal.context; + arkts.arktsGlobal.context; } catch (e) { // Do nothing } finally { const context: arkts.Context = arkts.Context.createFromString(source); - arktsGlobal.compilerContext = context; + arkts.arktsGlobal.compilerContext = context; } } -function resetConfig(cmd: string[]): void { +function resetConfig(cmd: string[]): arkts.Config { try { - arktsGlobal.config; - destroyConfig(); + arkts.arktsGlobal.config; + destroyConfig(arkts.arktsGlobal.config); } catch (e) { // Do nothing } finally { - const arkTSConfig: arkts.Config = arkts.Config.create(cmd); - arktsGlobal.config = arkTSConfig.peer; + const config = arkts.Config.create(cmd); + arkts.arktsGlobal.config = config.peer; + return config; } } -function destroyContext(): void { - arktsGlobal.es2panda._DestroyContext(arktsGlobal.context); -} - -function destroyConfig(): void { - arkts.destroyConfig(arktsGlobal.config); +function destroyContext(context: arkts.Context): void { + try { + arkts.arktsGlobal.es2panda._DestroyContext(context.peer); + } catch (e) { + // Do nothing + } } -function canProceedToState(state: arkts.Es2pandaContextState): boolean { - const stateToSkip: arkts.Es2pandaContextState[] = [ - arkts.Es2pandaContextState.ES2PANDA_STATE_SCOPE_INITED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ASM_GENERATED, - arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, - ]; - - if (state in stateToSkip) { - return false; +function destroyConfig(config: arkts.Config): void { + try { + arkts.destroyConfig(config); + } catch (e) { + // Do nothing } +} - const currState = arktsGlobal.es2panda._ContextState(arktsGlobal.context); - return currState < state; +function setUpSoPath(pandaSdkPath: string): void { + arkts.arktsGlobal.es2panda._SetUpSoPath(pandaSdkPath); } -export { initGlobal, resetContext, resetConfig, 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/parse-string.ts b/arkui-plugins/test/utils/parse-string.ts index 58430ca1db77b22a6369f97e17502c99f8eb0b3a..2db07c21f9936ba5433116b35f96eae12ba6cbce 100644 --- a/arkui-plugins/test/utils/parse-string.ts +++ b/arkui-plugins/test/utils/parse-string.ts @@ -14,9 +14,10 @@ */ function parseDumpSrc(str: string): string { - let _str: string = filterSource(str); + let _str: string = str; _str = cleanCopyRight(_str); _str = removeSpaceAndReturn(_str); + _str = replaceWithRandomNumber(_str); return _str; } @@ -28,7 +29,7 @@ function filterSource(text: string): string { } function cleanCopyRight(str: string): string { - const copyrightBlockRegex = /(?:\/\*.*Copyright \(c\) [- \d]+ Huawei Device Co\., Ltd\..*\*\/)/gs; + const copyrightBlockRegex = /(?:\/\*.*Copyright \([c|C]\) [- \d]+ [\w ]+\., Ltd\..*\*\/)/gs; return str.replace(copyrightBlockRegex, ''); } @@ -36,7 +37,13 @@ function cleanCopyRight(str: string): string { function removeSpaceAndReturn(str: string): string { const spaceAndReturnRegex = /^[\s\r]+/gm; - return str.replace(spaceAndReturnRegex, ''); + return str.replace(spaceAndReturnRegex, '').trim(); } -export { parseDumpSrc, filterSource, cleanCopyRight, removeSpaceAndReturn }; +function replaceWithRandomNumber(text: string): string { + return text + .replace(/(?<=__memo_id[\)+]?\s?\+\s?[\(+]?)\d+/g, () => '') + .replace(/(?<=gensym[_%]+)\d+/g, () => ''); +} + +export { parseDumpSrc, filterSource, cleanCopyRight, removeSpaceAndReturn, replaceWithRandomNumber }; diff --git a/arkui-plugins/test/utils/path-config.ts b/arkui-plugins/test/utils/path-config.ts index e322f28c8531ed586b4007201ce435d49de3cd1d..bb70824989c4387fdac8e5e32d586787a7299cdc 100644 --- a/arkui-plugins/test/utils/path-config.ts +++ b/arkui-plugins/test/utils/path-config.ts @@ -17,31 +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 getPandaSDKPath(): string { - if (!process.env.ETS2PANDA_PATH) { - throw new Error('Environment Error: ets2panda path is not defined'); - } - - return path.resolve(process.env.ETS2PANDA_PATH); +function getResourcePath(): string { + return path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'resource'); } function changeFileExtension(file: string, targetExt: string, originExt = ''): string { @@ -69,4 +70,4 @@ function ensurePathExists(filePath: string): void { } } -export { getRootPath, getPandaSDKPath, 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 74e60a41410eae048f9436e9981de17b91d10f9d..ef64a91bf0bc2c6f3e5774c1346bae9921441db5 100644 --- a/arkui-plugins/test/utils/plugin-driver.ts +++ b/arkui-plugins/test/utils/plugin-driver.ts @@ -13,14 +13,17 @@ * limitations under the License. */ -import * as arkts from '@koalaui/libarkts'; 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 c6b36d33d8ffd654b2a96ed6ee4325895f03bec9..1f1a6a59d2a256b82b3924c8a7b92cf00a4cd441 100644 --- a/arkui-plugins/test/utils/plugin-tester.ts +++ b/arkui-plugins/test/utils/plugin-tester.ts @@ -13,140 +13,175 @@ * limitations under the License. */ -import * as arkts from '@koalaui/libarkts'; -import { ArktsConfigBuilder, BuildConfig, CompileFileInfo, MockArktsConfigBuilder, ModuleInfo } from './artkts-config'; -import { MockPluginDriver, PluginDriver, stateName } from './plugin-driver'; -import { isNumber } from './safe-types'; -import { canProceedToState, initGlobal } from './global'; -import { insertPlugin, restartCompilerUptoState } from './compile'; -import { PluginExecutor, Plugins, PluginState } from '../../common/plugin-context'; +import { ArktsConfigBuilder, MockArktsConfigBuilder } from './artkts-config'; +import { MockPluginDriver } from './plugin-driver'; +import { + 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 = { beforeAll?: Parameters; - afterAll?: Parameters; beforeEach?: Parameters; afterEach?: Parameters; }; -export interface PluginTestContext { - script?: arkts.AstNode; - 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?: PluginTestContext; + 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.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 { - if (!this.cache) { - this.cache = {}; - } - if (index > arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED) { - return; - } - if (canProceedToState(index)) { - // TODO: this is a bug from compiler. - if (index > arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { - restartCompilerUptoState(index, true); + 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}`); } - arkts.proceedToState(index); } - if (plugin) { - insertPlugin(this.pluginDriver, plugin, index); - // TODO: add error/warning handling after plugin - this.cache.script = arkts.EtsScript.fromContext(); + } + + 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; } - const hook: SkipFirstParam | undefined = pluginHooks[key]; - if (!!hook) { - test(testName, hook[0]?.bind(this.cache), hook[1]); + 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; - - initGlobal(fileInfo, this.configBuilder.isDebug); - - 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 { - this.configBuilder.moduleInfos.forEach((moduleInfo) => { - moduleInfo.compileFileInfos.forEach((fileInfo) => { - 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[], @@ -155,25 +190,30 @@ 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.loadPluginDriver(plugins); - + const that = this; describe(this.describe, () => { if (testHooks?.beforeAll) { beforeAll(...testHooks.beforeAll); } - if (testHooks?.afterAll) { - afterAll(...testHooks.afterAll); - } if (testHooks?.beforeEach) { beforeEach(...testHooks.beforeEach); } if (testHooks?.afterEach) { afterEach(...testHooks.afterEach); } - this.traverseFile(testName, pluginHooks, options.stopAfter); + + afterAll(() => { + that.clear(); + }); + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..a770c96b00574186901146a05d6ba5eed9b019f7 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/builder-lambda-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 { BuilderLambdaTransformer } from '../../../ui-plugins/builder-lambda-translators/builder-lambda-transformer'; + +/** + * AfterCheck builder-lambda transform with no recheck AST. + */ +export const builderLambdaNoRecheck: Plugins = { + name: 'builder-lambda-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 BuilderLambdaTransformer(this.getProjectConfig()); + const programVisitor = new ProgramVisitor({ + pluginName: builderLambdaNoRecheck.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; + }, +}; diff --git a/arkui-plugins/test/utils/plugins/index.ts b/arkui-plugins/test/utils/plugins/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab232c217a4c678d522d4e2c7490ba8723e8c5fd --- /dev/null +++ b/arkui-plugins/test/utils/plugins/index.ts @@ -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. + */ + +// AfterParse +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'; +export * from './ui-no-recheck'; +export * from './recheck'; diff --git a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts new file mode 100644 index 0000000000000000000000000000000000000000..9396b34e5778d0fe20653018da0e6d8076ccb594 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts @@ -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 * as arkts from '@koalaui/libarkts'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { ProgramVisitor } from '../../../common/program-visitor'; +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. + */ +export const memoNoRecheck: Plugins = { + name: 'memo-no-recheck', + checked(this: PluginContext): arkts.EtsScript | undefined { + 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); + const parameterTransformer = new ParameterTransformer({ + positionalIdTracker, + }); + 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, + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2c3559df4107be301827c816f81442bba90b9abf --- /dev/null +++ b/arkui-plugins/test/utils/plugins/recheck.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 * as arkts from '@koalaui/libarkts'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; + +/** + * Recheck the current AST. This plugin can ONLY be used at afterCheck. + */ +export const recheck: Plugins = { + name: '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; + arkts.recheckSubtree(script); + return script; + } + return script; + }, +}; diff --git a/arkui-plugins/test/utils/plugins/struct-no-recheck.ts b/arkui-plugins/test/utils/plugins/struct-no-recheck.ts new file mode 100644 index 0000000000000000000000000000000000000000..87a7bdbd0cdec0fc276dacafed76f02403536644 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/struct-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 { StructTransformer } from '../../../ui-plugins/struct-translators/struct-transformer'; + +/** + * AfterCheck struct transform with no recheck AST. + */ +export const structNoRecheck: Plugins = { + name: 'struct-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 structTransformer = new StructTransformer(this.getProjectConfig()); + const programVisitor = new ProgramVisitor({ + pluginName: structNoRecheck.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [structTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; + }, +}; diff --git a/arkui-plugins/test/ut/ui-plugins/transform-struct-to-class.test.ts b/arkui-plugins/test/utils/plugins/struct-to-component.ts similarity index 31% rename from arkui-plugins/test/ut/ui-plugins/transform-struct-to-class.test.ts rename to arkui-plugins/test/utils/plugins/struct-to-component.ts index 8cac48c6f3dc05877bfdc7e4a5b6afd210b56ee7..ddb0281d2428a01a5939dcf29281100ab5650a04 100644 --- a/arkui-plugins/test/ut/ui-plugins/transform-struct-to-class.test.ts +++ b/arkui-plugins/test/utils/plugins/struct-to-component.ts @@ -13,79 +13,37 @@ * limitations under the License. */ -import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; -import { PluginTestContext, PluginTester } from '../../utils/plugin-tester'; -import { BuildConfig, mockBuildConfig } from '../../utils/artkts-config'; -import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../utils/path-config'; -import { parseDumpSrc } from '../../utils/parse-string'; import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; import { ComponentTransformer } from '../../../ui-plugins/component-transformer'; -const buildConfig: BuildConfig = mockBuildConfig(); -buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, 'struct-to-class.ets')]; - -const componentTransform: Plugins = { - name: 'component', - parsed(this: PluginContext) { - let program = this.getArkTSProgram(); - if (program) { - let script: arkts.EtsScript = program.astNode; - +/** + * AfterParse transform struct to component. + */ +export const structToComponent: Plugins = { + name: 'struct-to-component', + parsed(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 componentTransformer = new ComponentTransformer({ - arkui: '@koalaui.arkts-arkui.StructBase', + projectConfig: this.getProjectConfig(), }); - script = componentTransformer.visitor(script) as arkts.EtsScript; - arkts.setAllParents(script); + const programVisitor = new ProgramVisitor({ + pluginName: structToComponent.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + visitors: [componentTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; return script; } + return script; }, }; - -const pluginTester = new PluginTester('test component transformer', buildConfig); - -function testComponentTransformer(this: PluginTestContext): void { - const script: arkts.EtsScript = this.script as arkts.EtsScript; - const expectedScript: string = ` -import { StructBase } from \"@koalaui.arkts-arkui.StructBase\"; - -import { Component as Component } from \"@koalaui.arkts-arkui.Common\"; - -final class MyStateSample extends StructBase { - - public build() {} - - public constructor() {} - -} - -interface __Options_MyStateSample { - -} - -`; - expect(parseDumpSrc(script.dumpSrc())).toBe(parseDumpSrc(expectedScript)); -} - -pluginTester.run( - 'transform struct to class', - [componentTransform], - { - 'parsed:component': [testComponentTransformer], - }, - { - stopAfter: 'parsed', - }, - { - beforeEach: [ - () => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); - }, - ], - afterEach: [ - () => { - jest.spyOn(console, 'warn').mockRestore(); - }, - ], - } -); diff --git a/arkui-plugins/test/utils/plugins/ui-no-recheck.ts b/arkui-plugins/test/utils/plugins/ui-no-recheck.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fbc6c3fb4d7f04c8f2452611b3fb42abb0e07c5 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/ui-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 { CheckedTransformer } from '../../../ui-plugins/checked-transformer'; + +/** + * AfterCheck uiTransform with no recheck AST. + */ +export const uiNoRecheck: Plugins = { + name: 'ui-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 checkedTransformer = new CheckedTransformer(this.getProjectConfig()); + const programVisitor = new ProgramVisitor({ + pluginName: uiNoRecheck.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [checkedTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + 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 7efa8486d68672293746d886e8b6a0493ab2d383..a7e077cb8bed88d599bd46169b02949a61948b82 100644 --- a/arkui-plugins/tsconfig.build.json +++ b/arkui-plugins/tsconfig.build.json @@ -17,9 +17,11 @@ "removeComments": false }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", + "./ui-syntax-plugins/**/*.ts", "./interop-plugins/**/*.ts", ], "exclude": [ diff --git a/arkui-plugins/tsconfig.json b/arkui-plugins/tsconfig.json index eab872848bc93f9fba42409d0f0c2a2dffc6d4d4..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", @@ -15,12 +15,16 @@ "noEmitOnError": true, "strict": true, "skipLibCheck": true, - "removeComments": false + "removeComments": false, + "isolatedModules": true, + "esModuleInterop": true }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", + "./ui-syntax-plugins/**/*.ts", "./interop-plugins/**/*.ts", "./path.ts", "./test/ut/**/*.ts", 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 a0d1134c5e98c1943389d5529f34b353e5c4a8b7..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 @@ -12,263 +12,39 @@ * 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 { BuilderLambdaNames } from '../utils'; -import { filterDefined, removeAnnotationByName, backingField } from '../../common/arkts-utils'; -import { DecoratorNames } from '../property-translators/utils'; -import { - BuilderLambdaDeclInfo, - isBuilderLambda, - builderLambdaFunctionName, - findBuilderLambdaDeclInfo, - isBuilderLambdaMethodDecl, - replaceBuilderLambdaDeclMethodName, - isBuilderLambdaFunctionCall, - callIsGoodForBuilderLambda, - builderLambdaTypeName, - builderLambdaMethodDeclType, - builderLambdaType, -} from './utils'; +import { ImportCollector } from '../../common/import-collector'; +import { isBuilderLambda, isBuilderLambdaMethodDecl } from './utils'; import { factory } from './factory'; +import { ProjectConfig } from '../../common/plugin-context'; -// TODO: very time-consuming... -function updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode, isExternal?: boolean): arkts.AstNode { - if (arkts.isIfStatement(statement)) { - const alternate = !!statement.alternate - ? updateIfElseContentBodyInBuilderLambda(statement.alternate, isExternal) - : statement.alternate; - const consequence = updateIfElseContentBodyInBuilderLambda(statement.consequent, isExternal); - return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); - } - if (arkts.isBlockStatement(statement)) { - return arkts.factory.updateBlock( - statement, - statement.statements.map((st) => updateContentBodyInBuilderLambda(st, isExternal)) - ); - } - return statement; -} - -function updateContentBodyInBuilderLambda(statement: arkts.Statement, isExternal?: boolean): arkts.Statement { - if ( - arkts.isExpressionStatement(statement) && - arkts.isCallExpression(statement.expression) && - isBuilderLambda(statement.expression, isExternal) - ) { - return arkts.factory.updateExpressionStatement(statement, transformBuilderLambda(statement.expression)); - } - // TODO: very time-consuming... - if (arkts.isIfStatement(statement)) { - return updateIfElseContentBodyInBuilderLambda(statement, isExternal); - } - - return statement; -} - -function builderLambdaReplace(leaf: arkts.CallExpression): arkts.Identifier | arkts.MemberExpression | undefined { - if (!callIsGoodForBuilderLambda(leaf)) { - return undefined; - } - const node = leaf.expression; - const funcName = builderLambdaFunctionName(leaf); - if (!funcName) { - return undefined; - } - if (arkts.isIdentifier(node)) { - return arkts.factory.createIdentifier(funcName); - } - if (arkts.isMemberExpression(node)) { - return arkts.factory.createMemberExpression( - node.object, - arkts.factory.createIdentifier(funcName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - node.computed, - node.optional - ); - } - return undefined; -} - -function createOrUpdateArgInBuilderLambda( - arg: arkts.Expression | undefined, - isExternal?: boolean, - typeName?: string, - fallback?: arkts.AstNode -): arkts.AstNode { - if (!arg) { - return fallback ?? arkts.factory.createUndefinedLiteral(); - } - if (arkts.isArrowFunctionExpression(arg)) { - return processArgArrowFunction(arg, isExternal); - } - if (arkts.isTSAsExpression(arg)) { - return processArgTSAsExpression(arg, typeName!); - } - return arg; -} - -function processArgArrowFunction( - arg: arkts.ArrowFunctionExpression, - isExternal?: 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) => updateContentBodyInBuilderLambda(st, isExternal)) - ) - : undefined, - arkts.FunctionSignature.createFunctionSignature(func.typeParams, func.params, func.returnTypeAnnotation, false), - func.flags, - func.modifiers - ); - return arkts.factory.updateArrowFunction(arg, updateFunc); -} - -function updateParameterPassing( - prop: arkts.Property, - index: number, - currentStructInfo: arkts.StructInfo, - properties: arkts.Property[] -): void { - if ( - prop.key && - prop.value && - 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.properties.includes(DecoratorNames.LINK)) { - properties[index] = arkts.Property.updateProperty( - prop, - arkts.factory.createIdentifier(backingField(prop.key.name)), - factory.updateBackingMember(prop.value, prop.value.property.name) - ); - } - } -} - -function processArgTSAsExpression(arg: arkts.TSAsExpression, typeName: string): arkts.TSAsExpression { - if (!arg.expr || !arkts.isObjectExpression(arg.expr)) { - return arg; - } - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName!); - const properties = arg.expr.properties as arkts.Property[]; - properties.forEach((prop, index) => { - updateParameterPassing(prop, index, currentStructInfo, properties); - }); - const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( - arg.expr, - arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - properties, - false - ); - return arkts.TSAsExpression.updateTSAsExpression(arg, updatedExpr, arg.typeAnnotation, arg.isConst); -} - -function generateArgsInBuilderLambda( - leaf: arkts.CallExpression, - lambdaBody: arkts.Identifier | arkts.CallExpression, - declInfo: BuilderLambdaDeclInfo, - isExternal?: boolean -): (arkts.AstNode | undefined)[] { - const { params } = declInfo; - const typeNode: arkts.TypeNode | undefined = builderLambdaType(leaf); - const typeName: string | undefined = builderLambdaTypeName(leaf); - const args: (arkts.AstNode | undefined)[] = [factory.createStyleArgInBuilderLambda(lambdaBody, typeNode)]; - let index = 0; - while (index < params.length) { - const isReusable: boolean = typeName - ? arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName).isReusable - : false; - if (isReusable && index === params.length - 1) { - const reuseId = arkts.factory.createStringLiteral(typeName!); - args.push(createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), isExternal, typeName, reuseId)); - } else { - args.push(createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), isExternal, typeName)); - } - index++; - } - return args; -} - -function transformBuilderLambda(node: arkts.CallExpression, isExternal?: boolean): arkts.AstNode { - let instanceCalls: arkts.CallExpression[] = []; - let leaf: arkts.CallExpression = node; +export class BuilderLambdaTransformer extends AbstractVisitor { + projectConfig: ProjectConfig | undefined; - while ( - true && - arkts.isMemberExpression(leaf.expression) && - arkts.isIdentifier(leaf.expression.property) && - arkts.isCallExpression(leaf.expression.object) - ) { - instanceCalls.push(arkts.factory.createCallExpression(leaf.expression.property, undefined, leaf.arguments)); - leaf = leaf.expression.object; + constructor(projectConfig: ProjectConfig | undefined) { + super(); + this.projectConfig = projectConfig; } - const replace: arkts.Identifier | arkts.MemberExpression | undefined = builderLambdaReplace(leaf); - const declInfo: BuilderLambdaDeclInfo | undefined = findBuilderLambdaDeclInfo(leaf); - if (!replace || !declInfo) { - return node; - } - let lambdaBody: arkts.Identifier | arkts.CallExpression | undefined; - if (instanceCalls.length > 0) { - instanceCalls = instanceCalls.reverse(); - lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); - instanceCalls.forEach((call) => { - if (!arkts.isIdentifier(call.expression)) { - throw new Error('call expression should be identifier'); - } - lambdaBody = factory.createStyleLambdaBody(lambdaBody!, call); - }); + reset(): void { + super.reset(); + ImportCollector.getInstance().reset(); } - const args: (arkts.AstNode | undefined)[] = generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo, isExternal); - return arkts.factory.updateCallExpression(node, replace, undefined, filterDefined(args)); -} - -function transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.AstNode { - const func: arkts.ScriptFunction = node.scriptFunction; - const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); - const typeNode: arkts.TypeNode | undefined = builderLambdaMethodDeclType(node); - const styleArg: arkts.ETSParameterExpression = factory.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall); - return factory.updateBuilderLambdaMethodDecl( - node, - styleArg, - removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), - replaceBuilderLambdaDeclMethodName(node.name.name) - ); -} -export class BuilderLambdaTransformer extends AbstractVisitor { - visitEachChild(node: arkts.AstNode): arkts.AstNode { - if (arkts.isCallExpression(node) && isBuilderLambda(node, this.isExternal)) { - return node; + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { + const lambda = factory.transformBuilderLambda(beforeChildren); + return this.visitEachChild(lambda); } - if (arkts.isMethodDefinition(node) && isBuilderLambdaMethodDecl(node, this.isExternal)) { - return node; + if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { + const lambda = factory.transformBuilderLambdaMethodDecl(beforeChildren); + return this.visitEachChild(lambda); } - - return super.visitEachChild(node); - } - - visitor(beforeChildren: arkts.AstNode): arkts.AstNode { const node = this.visitEachChild(beforeChildren); - - if (arkts.isCallExpression(node) && isBuilderLambda(node, this.isExternal)) { - const lambda = transformBuilderLambda(node, this.isExternal); - return lambda; - } - if (arkts.isMethodDefinition(node) && isBuilderLambdaMethodDecl(node, this.isExternal)) { - const lambda = transformBuilderLambdaMethodDecl(node); - return lambda; + 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/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 5cf2d12408fef6534e6e53f78101d0729bff8382..55997c0c32272ec3a0b5f7f2af952c2cc6f610f3 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -15,26 +15,64 @@ import * as arkts from '@koalaui/libarkts'; import { BuilderLambdaNames } from '../utils'; -import { annotation, backingField } from '../../common/arkts-utils'; +import { + backingField, + filterDefined, + isDecoratorAnnotation, + removeAnnotationByName, + forEachArgWithParam, +} from '../../common/arkts-utils'; +import { + BuilderLambdaDeclInfo, + builderLambdaFunctionName, + builderLambdaMethodDeclType, + callIsGoodForBuilderLambda, + collectComponentAttributeImport, + findBuilderLambdaDecl, + findBuilderLambdaDeclInfo, + isBuilderLambda, + isBuilderLambdaFunctionCall, + isSafeType, + replaceBuilderLambdaDeclMethodName, + getDecalTypeFromValue, + hasBindableProperty, + isDoubleDollarCall, + InstanceCallInfo, + isStyleChainedCall, + isStyleWithReceiverCall, + builderLambdaType, + BuilderLambdaSecondLastArgInfo, + buildSecondLastArgInfo, +} from './utils'; +import { isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; +import { factory as PropertyFactory } from '../property-translators/factory'; +import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, DecoratorNames } from '../../common/predefines'; +import { ImportCollector } from '../../common/import-collector'; +import { addMemoAnnotation, collectMemoableInfoInParameter } from '../../collectors/memo-collectors/utils'; 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 - ): arkts.AstNode { + 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 ), @@ -47,27 +85,76 @@ export class factory { node, node.kind, arkts.factory.updateIdentifier(node.name, newName ?? node.name.name), - arkts.factory.createFunctionExpression(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[] { + 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?.clone() + : 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]; + } + + /* + * 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 objExp: arkts.ObjectExpression = arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [factory.generateValueProperty(bindableArg), factory.generateOnChangeArrowFunc(bindableArg, valueType)], + false ); + return arkts.factory.createTSAsExpression(objExp, factory.createBindableType(valueType), false); } /* * create style instance call, e.g. `instance.margin(10)`. */ - static createStyleLambdaBody(lambdaBody: arkts.AstNode, call: arkts.CallExpression): arkts.CallExpression { - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( + static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo): arkts.CallExpression { + if (!callInfo.isReceiver) { + const newArgs: arkts.Expression[] = factory.getTransformedStyle(callInfo.call); + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + lambdaBody, + callInfo.call.expression, + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + newArgs.map((arg) => { + if (arkts.isArrowFunctionExpression(arg)) { + return this.processArgArrowFunction(arg); + } + return arg; + }) + ); + } else { + return arkts.factory.createCallExpression(callInfo.call.expression, callInfo.call.typeArguments, [ lambdaBody, - call.expression, - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - call.arguments - ); + ...callInfo.call.arguments.slice(1), + ]); + } } /* @@ -89,20 +176,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, typeNode), + 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( @@ -117,7 +209,7 @@ export class factory { arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - return arkts.factory.createArrowFunction(func); + return addMemoAnnotation(arkts.factory.createArrowFunction(func)); } /* @@ -140,23 +232,447 @@ 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 + 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; + } + + /** + * If a builder lambda's argument is an arrow function, + * then transform any builder lambda in the function body. + */ + static processArgArrowFunction(arg: arkts.ArrowFunctionExpression): 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)) + ) + : undefined, + arkts.FunctionSignature.createFunctionSignature( + func.typeParams, + func.params, + func.returnTypeAnnotation, + false + ), + func.flags, + func.modifiers + ); + return arkts.factory.updateArrowFunction(arg, updateFunc); + } + + /** + * transform options argument in a builder lambda call. + */ + static processOptionsArg(arg: T, typeName: string): T { + let expr: arkts.ObjectExpression | undefined; + if (arkts.isTSAsExpression(arg) && !!arg.expr && arkts.isObjectExpression(arg.expr)) { + expr = arg.expr; + } else if (arkts.isObjectExpression(arg)) { + expr = arg; + } + if (!expr) { + return arg; + } + const properties = (expr.properties as arkts.Property[]).map((p) => factory.updatePropertiesInOptions(p)); + const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( + expr, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + properties, + false + ); + if (arkts.isTSAsExpression(arg)) { + return arkts.TSAsExpression.updateTSAsExpression(arg, updatedExpr, arg.typeAnnotation, arg.isConst) as T; + } + return updatedExpr as T; + } + + 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; + } + const returnType: arkts.TypeNode | undefined = decl.scriptFunction.returnTypeAnnotation; + const isBindable: boolean = !!returnType && hasBindableProperty(returnType, BindableDecl.BINDABLE); + 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 (isBindable && 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) + ) { + return arkts.Property.updateProperty( + prop, + arkts.factory.createIdentifier(backingField(prop.key.name)), + factory.updateBackingMember(prop.value, prop.value.property.name) ); } - parameter.annotations = [annotation('memo')]; - return parameter; + return prop; + } + + /** + * create or update arguments in a builder lambda call. + * If the corresponding argument is not provided, fill-in an `undefined` to it. + */ + static createOrUpdateArgInBuilderLambda( + fallback: arkts.AstNode | undefined, + arg: arkts.Expression | undefined, + typeName?: string, + canAddMemo?: boolean + ): arkts.AstNode | undefined { + if (!arg) { + return fallback; + } + if (arkts.isArrowFunctionExpression(arg)) { + const newNode = this.processArgArrowFunction(arg); + 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)) { + return this.processOptionsArg(arg, typeName!); + } + 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 + ): (arkts.AstNode | undefined)[] { + 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. + */ + static updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode): arkts.AstNode { + if (arkts.isIfStatement(statement)) { + const alternate = !!statement.alternate + ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate) + : statement.alternate; + const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent); + return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); + } + if (arkts.isBlockStatement(statement)) { + return arkts.factory.updateBlock( + statement, + statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st)) + ); + } + return statement; + } + + /** + * update trailing lambda contents in a builder lambda call. + */ + static updateContentBodyInBuilderLambda(statement: arkts.Statement): arkts.Statement { + if ( + arkts.isExpressionStatement(statement) && + arkts.isCallExpression(statement.expression) && + isBuilderLambda(statement.expression) + ) { + return arkts.factory.updateExpressionStatement( + statement, + this.transformBuilderLambda(statement.expression) + ); + } + if (arkts.isIfStatement(statement)) { + return this.updateIfElseContentBodyInBuilderLambda(statement); + } + + return statement; + } + + /** + * replace function call's name to the corresponding transformed name. + */ + static builderLambdaReplace(leaf: arkts.CallExpression): arkts.Identifier | arkts.MemberExpression | undefined { + if (!callIsGoodForBuilderLambda(leaf)) { + return undefined; + } + const node = leaf.expression; + const funcName = builderLambdaFunctionName(leaf); + if (!funcName) { + return undefined; + } + if (arkts.isIdentifier(node)) { + return arkts.factory.createIdentifier(funcName); + } + if (arkts.isMemberExpression(node)) { + return arkts.factory.createMemberExpression( + node.object, + arkts.factory.createIdentifier(funcName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + node.computed, + node.optional + ); + } + return undefined; + } + + /** + * transform `@ComponentBuilder` in declared methods. + */ + 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 newOverloads: arkts.MethodDefinition[] = node.overloads.map((method) => + factory.transformBuilderLambdaMethodDecl(method) + ); + + const newNode = this.updateBuilderLambdaMethodDecl( + node, + [this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall)], + removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), + replaceBuilderLambdaDeclMethodName(node.name.name), + externalSourceName + ).setOverloads(newOverloads); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; + } + + /** + * transform `.animation(...)` to `.animationStart(...) and .animationStop(...)` + */ + static updateAnimation(instanceCalls: InstanceCallInfo[]): void { + let lastAniIdx = 0; + let curIdx = 0; + + while (curIdx < instanceCalls.length) { + if (instanceCalls[curIdx].isReceiver) { + curIdx++; + continue; + } + const property: arkts.Identifier = instanceCalls[curIdx].call.expression as arkts.Identifier; + if (property.name === AnimationNames.ANIMATION) { + const aniStart: arkts.CallExpression = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(AnimationNames.ANIMATION_START), + undefined, + instanceCalls[curIdx].call.arguments + ); + const aniStop: arkts.CallExpression = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(AnimationNames.ANIMATION_STOP), + undefined, + instanceCalls[curIdx].call.arguments.map((arg) => arg.clone()) + ); + instanceCalls.splice(lastAniIdx, 0, { isReceiver: false, call: aniStart }); + instanceCalls[curIdx + 1] = { isReceiver: false, call: aniStop }; + curIdx += 2; + lastAniIdx = curIdx; + } else { + curIdx++; + } + } + } + + /** + * transform `@ComponentBuilder` in non-declared calls. + */ + static transformBuilderLambda(node: arkts.CallExpression): arkts.AstNode { + let instanceCalls: InstanceCallInfo[] = []; + let leaf: arkts.CallExpression = node; + + while (isStyleChainedCall(leaf) || isStyleWithReceiverCall(leaf)) { + if (isStyleChainedCall(leaf)) { + instanceCalls.push({ + isReceiver: false, + call: arkts.factory.createCallExpression( + (leaf.expression as arkts.MemberExpression).property, + leaf.typeArguments, + leaf.arguments + ), + }); + leaf = (leaf.expression as arkts.MemberExpression).object as arkts.CallExpression; + } + + if (isStyleWithReceiverCall(leaf)) { + instanceCalls.push({ + isReceiver: true, + call: arkts.factory.createCallExpression(leaf.expression, leaf.typeArguments, leaf.arguments), + }); + leaf = leaf.arguments[0] as arkts.CallExpression; + } + } + + const decl: arkts.AstNode | undefined = findBuilderLambdaDecl(leaf); + if (!decl) { + return node; + } + + const replace: arkts.Identifier | arkts.MemberExpression | undefined = this.builderLambdaReplace(leaf); + const declInfo: BuilderLambdaDeclInfo | undefined = findBuilderLambdaDeclInfo(decl); + if (!replace || !declInfo) { + return node; + } + + let lambdaBody: arkts.Identifier | arkts.CallExpression | undefined; + if (instanceCalls.length > 0) { + 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); + }); + } + + 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; + } + + /* + * update bindableProperty, e.g. `text: $$(this.text)` => `text: { value: xxx , onChange: xxx }`. + */ + static updateBindableProperty(prop: arkts.Property, type?: arkts.TypeNode): arkts.Property { + let res: arkts.Property[] = []; + let valueType: arkts.TypeNode; + if ( + prop.value && + arkts.isCallExpression(prop.value) && + prop.value.arguments && + prop.value.arguments.length === 1 + ) { + let bindableArg = prop.value.arguments[0]; + valueType = getDecalTypeFromValue(bindableArg); + res.push( + factory.generateValueProperty(bindableArg), + factory.generateOnChangeArrowFunc(bindableArg, valueType) + ); + } else { + return prop; + } + const asObjProp: arkts.TSAsExpression = arkts.factory.createTSAsExpression( + arkts.ObjectExpression.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + res, + false + ), + factory.createBindableType(valueType), + false + ); + return arkts.factory.updateProperty(prop, prop.key, asObjProp); + } + + /* + * generate `value: ` in object. + */ + static generateValueProperty(bindableArg: arkts.Expression): arkts.Property { + return arkts.factory.createProperty(arkts.factory.createIdentifier('value'), bindableArg.clone()); + } + + /* + * generate `onChange: (value) => = value` in object. + */ + static generateOnChangeArrowFunc(bindableArg: arkts.Expression, valueType: arkts.TypeNode): arkts.Property { + return arkts.factory.createProperty( + arkts.factory.createIdentifier('onChange'), + PropertyFactory.createArrowFunctionWithParamsAndBody( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('value', valueType.clone()), + undefined + ), + ], + undefined, + false, + [ + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + bindableArg.clone(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier('value') + ) + ), + ] + ) + ); + } + + /* + * 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(transformedKey), + arkts.factory.createTSTypeParameterInstantiation([valueType.clone()]) + ) + ); } } diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index b31f6e284126e8d90e116efbdd1f4a1042e44108..ab738bc3d0d3b8ad0b9c0621be0fb9fbe564109c 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -14,16 +14,56 @@ */ import * as arkts from '@koalaui/libarkts'; -import { isAnnotation } from '../../common/arkts-utils'; -import { BuilderLambdaNames } 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 type BuilderLambdaAstNode = arkts.ScriptFunction | arkts.ETSParameterExpression | arkts.FunctionDeclaration; +export type InstanceCallInfo = { + isReceiver: boolean; + call: arkts.CallExpression; +}; + +export type BuilderLambdaArgInfo = { + isFunctionCall: boolean; +}; + +export type BuilderLambdaReusableArgInfo = { + isReusable?: boolean; + reuseId?: string; +}; + +export type BuilderLambdaSecondLastArgInfo = BuilderLambdaArgInfo & BuilderLambdaReusableArgInfo; + +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 @@ -44,14 +84,64 @@ export function builderLambdaArgumentName(annotation: arkts.AnnotationUsage): st return property.value.str; } +export function isBuilderLambda(node: arkts.AstNode): boolean { + const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node); + if (!builderLambdaCall) { + return arkts.isCallExpression(node) && node.arguments.length > 0 && isBuilderLambda(node.arguments[0]); + } + return !!builderLambdaCall; +} + /** - * Determine whether it is a custom component. + * Determine whether it is a function with receiver method definition. * - * @param node class declaration node + * @param node method definition node */ -export function isBuilderLambda(node: arkts.AstNode, isExternal?: boolean): boolean { - const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node); - return !!builderLambdaCall; +export function isFunctionWithReceiver(node: arkts.MethodDefinition): boolean { + if (node.scriptFunction && arkts.isScriptFunction(node.scriptFunction)) { + return node.scriptFunction.hasReceiver; + } + return false; +} + +/** + * Determine whether it is a function with receiver call. + * + * @param node identifier node + */ +export function isFunctionWithReceiverCall(node: arkts.Identifier): boolean { + const decl: arkts.AstNode | undefined = arkts.getDecl(node); + if (decl && arkts.isMethodDefinition(decl)) { + return isFunctionWithReceiver(decl); + } + return false; +} + +/** + * Determine whether it is a style chained call. + * + * @param node call expression node + */ +export function isStyleChainedCall(node: arkts.CallExpression): boolean { + return ( + arkts.isMemberExpression(node.expression) && + arkts.isIdentifier(node.expression.property) && + arkts.isCallExpression(node.expression.object) + ); +} + +/** + * Determine whether it is a style function with receiver call. + * + * @param node call expression node + */ +export function isStyleWithReceiverCall(node: arkts.CallExpression): boolean { + return ( + arkts.isIdentifier(node.expression) && + isFunctionWithReceiverCall(node.expression) && + !!node.arguments.length && + arkts.isCallExpression(node.arguments[0]) + ); } /** @@ -66,15 +156,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; } @@ -89,7 +176,7 @@ export function getDeclForBuilderLambdaMethodDecl( return undefined; } -export function getDeclForBuilderLambda(node: arkts.AstNode, isExternal?: boolean): arkts.AstNode | undefined { +export function getDeclForBuilderLambda(node: arkts.AstNode): arkts.AstNode | undefined { if (!node || !arkts.isCallExpression(node)) { return undefined; } @@ -129,6 +216,13 @@ export function isBuilderLambdaCall(node: arkts.CallExpression | arkts.Identifie } if (arkts.isMethodDefinition(decl)) { + if (isFunctionWithReceiver(decl)) { + return ( + arkts.isCallExpression(node) && + node.arguments.length > 0 && + !!getDeclForBuilderLambda(node.arguments[0]) + ); + } return isBuilderLambdaMethod(decl); } if (arkts.isFunctionExpression(decl)) { @@ -206,6 +300,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; } @@ -225,15 +324,19 @@ export function isParameterPassing(prop: arkts.Property): boolean | undefined { ); } -export function findBuilderLambdaDeclInfo(node: arkts.CallExpression): BuilderLambdaDeclInfo | undefined { - const decl = findBuilderLambdaDecl(node); +export function findBuilderLambdaDeclInfo(decl: arkts.AstNode | undefined): BuilderLambdaDeclInfo | undefined { 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 }; + return { isFunctionCall, params, returnType, moduleName }; } return undefined; @@ -257,15 +360,15 @@ export function callIsGoodForBuilderLambda(leaf: arkts.CallExpression): boolean return arkts.isIdentifier(node) || arkts.isMemberExpression(node); } -export function builderLambdaType(leaf: arkts.CallExpression): arkts.TypeNode | undefined { - const name: string | undefined = builderLambdaTypeName(leaf); - if (!name) { - return undefined; +export function isSafeType(type: arkts.TypeNode | undefined): boolean { + if (!type) { + return false; } - // TODO: it should be the return type of the function annotated with the @BuilderLambda - return arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(`${name}Attribute`)) - ); + // type can be generic (not safe) if includes any type params in a type reference. + if (arkts.isETSTypeReference(type) && !!type.part && !!type.part.typeParams) { + return false; + } + return true; } export function builderLambdaMethodDeclType(method: arkts.MethodDefinition): arkts.TypeNode | undefined { @@ -275,17 +378,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; } @@ -307,3 +410,133 @@ 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, + ignoreDecl: boolean = false +): value is arkts.CallExpression { + if (!arkts.isCallExpression(value)) { + return false; + } + if ( + !(!!value.expression && arkts.isIdentifier(value.expression) && value.expression.name === Dollars.DOLLAR_DOLLAR) + ) { + 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()`. + * + * @param value type node + */ +export function getDecalTypeFromValue(value: arkts.Expression): arkts.TypeNode { + const decl: arkts.AstNode | undefined = arkts.getDecl(value); + if (!decl || !arkts.isClassProperty(decl)) { + throw new Error('cannot get declaration'); + } + if (isArrayType(decl.typeAnnotation!)) { + return getElementTypeFromArray(decl.typeAnnotation!)!; + } + return decl.typeAnnotation!; +} + +/** + * Determine whether `` is array type, e.g. `xxx[]` or `Array`. + * + * @param type type node + */ +export function isArrayType(type: arkts.TypeNode): boolean { + return ( + arkts.isTSArrayType(type) || + (arkts.isETSTypeReference(type) && + !!type.part && + arkts.isETSTypeReferencePart(type.part) && + !!type.part.name && + arkts.isIdentifier(type.part.name) && + type.part.name.name === 'Array') + ); +} + +/** + * get element type from array type node ``. + * + * @param arrayType array type node + */ +export function getElementTypeFromArray(arrayType: arkts.TypeNode): arkts.TypeNode | undefined { + if (arkts.isTSArrayType(arrayType)) { + return arrayType.elementType?.clone(); + } else if ( + arkts.isETSTypeReference(arrayType) && + !!arrayType.part && + arkts.isETSTypeReferencePart(arrayType.part) && + !!arrayType.part.typeParams && + arrayType.part.typeParams.params.length + ) { + return arrayType.part.typeParams.params[0].clone(); + } + 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); + } +} diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index e544623b2ec1b20e57a10807f5cdec76064eb105..0dbc8b4458dfb19a07dc4ca473510db86443c33a 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -17,453 +17,138 @@ 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, removeAnnotationByName, backingField } from '../common/arkts-utils'; -import { - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, - BuilderLambdaNames, -} 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/utils'; +import { checkCustomDialogController, insertImportDeclaration, transformDeclaration } from './customdialog'; +import { LogCollector } from '../common/log-collector'; import { - ScopeInfo, - isCustomComponentClass, - isKnownMethodDefinition, - isEtsGlobalClass, - isReourceNode, + CustomComponentScopeInfo, + initResourceInfo, + loadBuildJson, + LoaderJson, + ResourceInfo, + ScopeInfoCollection, + isForEachDecl } from './struct-translators/utils'; -import { - isBuilderLambda, - isBuilderLambdaMethodDecl, - isBuilderLambdaFunctionCall, - builderLambdaMethodDeclType, - replaceBuilderLambdaDeclMethodName, - findBuilderLambdaDeclInfo, - callIsGoodForBuilderLambda, - builderLambdaFunctionName, - BuilderLambdaDeclInfo, - builderLambdaType, - builderLambdaTypeName, -} from './builder-lambda-translators/utils'; -import { isEntryWrapperClass } from './entry-translators/utils'; -import { classifyProperty, PropertyTranslator } from './property-translators'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from './utils'; +import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; export class CheckedTransformer extends AbstractVisitor { - private scopeInfos: ScopeInfo[] = []; + private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { super.reset(); - this.scopeInfos = []; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); + LogCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfos.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.scopeInfos.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfos.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.scopeInfos.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfos.pop(); + if ( + arkts.isClassDeclaration(node) && + this.scope.customComponents.length > 0 && + isCustomComponentClass(node, this.scope.customComponents[this.scope.customComponents.length - 1]) + ) { + this.scope.customComponents.pop(); } } - visitEachChild(node: arkts.AstNode): arkts.AstNode { - if (arkts.isCallExpression(node) && isBuilderLambda(node, this.isExternal)) { - return node; - } - if (arkts.isMethodDefinition(node) && isBuilderLambdaMethodDecl(node, this.isExternal)) { - return node; + isCustomDialogController(): boolean { + if (this.isExternal && this.externalSourceName === 'arkui.component.customDialogController') { + return true; } + return false; + } - return super.visitEachChild(node); + processCustomDialogController(node: arkts.AstNode): arkts.AstNode { + if (arkts.isEtsScript(node)) { + insertImportDeclaration(this.program); + } + if (arkts.isClassDeclaration(node) && node.definition && node.definition.ident && + node.definition.ident.name === 'CustomDialogController') { + return transformDeclaration(node as arkts.ClassDeclaration); + } + return node; } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); - const node = this.visitEachChild(beforeChildren); - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - let scope: ScopeInfo | undefined; - if (this.scopeInfos.length > 0) { - scope = this.scopeInfos[this.scopeInfos.length - 1]; - } - const newClass: arkts.ClassDeclaration = tranformClassMembers( - node, - arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE), - scope + if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { + const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); + return this.visitEachChild(lambda); + } else if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { + const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl( + beforeChildren, + this.externalSourceName ); + return this.visitEachChild(lambda); + } + const node = this.visitEachChild(beforeChildren); + findAndCollectMemoableNode(node); + if (this.isCustomDialogController()) { + return this.processCustomDialogController(node); + } else 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 (arkts.isCallExpression(node) && isBuilderLambda(node, this.isExternal)) { - const lambda = transformBuilderLambda(node, this.isExternal); - return lambda; - } else if (arkts.isMethodDefinition(node) && isBuilderLambdaMethodDecl(node, this.isExternal)) { - const lambda = transformBuilderLambdaMethodDecl(node); - return lambda; + } else if (arkts.isClassDeclaration(node)) { + return structFactory.transformNormalClass(node); + } 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.isBlockStatement(node)) { + return checkCustomDialogController(node); } - return node; - } -} - -function tranformClassMembers( - node: arkts.ClassDeclaration, - isDecl?: boolean, - scope?: ScopeInfo -): 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); -} - -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 structFactory.transformBuildMethodWithOriginBuild( - member, - classTypeName ?? className, - classOptionsName ?? getCustomComponentOptionsName(className), - isDecl - ); - } - return member; -} - -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: ScopeInfo -): 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); -} - -function transformBuilderLambdaMethodDecl(node: arkts.MethodDefinition): arkts.AstNode { - const func: arkts.ScriptFunction = node.scriptFunction; - const isFunctionCall: boolean = isBuilderLambdaFunctionCall(node); - const typeNode: arkts.TypeNode | undefined = builderLambdaMethodDeclType(node); - const styleArg: arkts.ETSParameterExpression = builderLambdaFactory.createStyleArgInBuilderLambdaDecl( - typeNode, - isFunctionCall - ); - return builderLambdaFactory.updateBuilderLambdaMethodDecl( - node, - styleArg, - removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), - replaceBuilderLambdaDeclMethodName(node.name.name) - ); -} - -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')]); + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); + LogCollector.getInstance().emitLogInfo(); } - return member; - }); - return node; -} - -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); -} - -function transformBuilderLambda(node: arkts.CallExpression, isExternal?: boolean): arkts.AstNode { - let instanceCalls: arkts.CallExpression[] = []; - let leaf: arkts.CallExpression = node; - - while ( - true && - arkts.isMemberExpression(leaf.expression) && - arkts.isIdentifier(leaf.expression.property) && - arkts.isCallExpression(leaf.expression.object) - ) { - instanceCalls.push(arkts.factory.createCallExpression(leaf.expression.property, undefined, leaf.arguments)); - leaf = leaf.expression.object; - } - - const replace: arkts.Identifier | arkts.MemberExpression | undefined = builderLambdaReplace(leaf); - const declInfo: BuilderLambdaDeclInfo | undefined = findBuilderLambdaDeclInfo(leaf); - if (!replace || !declInfo) { return node; } - let lambdaBody: arkts.Identifier | arkts.CallExpression | undefined; - if (instanceCalls.length > 0) { - instanceCalls = instanceCalls.reverse(); - lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); - instanceCalls.forEach((call) => { - if (!arkts.isIdentifier(call.expression)) { - throw new Error('call expression should be identifier'); - } - lambdaBody = builderLambdaFactory.createStyleLambdaBody(lambdaBody!, call); - }); - } - const args: (arkts.AstNode | undefined)[] = generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo, isExternal); - return arkts.factory.updateCallExpression(node, replace, undefined, filterDefined(args)); -} - -function builderLambdaReplace(leaf: arkts.CallExpression): arkts.Identifier | arkts.MemberExpression | undefined { - if (!callIsGoodForBuilderLambda(leaf)) { - return undefined; - } - const node = leaf.expression; - const funcName = builderLambdaFunctionName(leaf); - if (!funcName) { - return undefined; - } - if (arkts.isIdentifier(node)) { - return arkts.factory.createIdentifier(funcName); - } - if (arkts.isMemberExpression(node)) { - return arkts.factory.createMemberExpression( - node.object, - arkts.factory.createIdentifier(funcName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - node.computed, - node.optional - ); - } - return undefined; -} - -function generateArgsInBuilderLambda( - leaf: arkts.CallExpression, - lambdaBody: arkts.Identifier | arkts.CallExpression, - declInfo: BuilderLambdaDeclInfo, - isExternal?: boolean -): (arkts.AstNode | undefined)[] { - const { params } = declInfo; - const typeNode: arkts.TypeNode | undefined = builderLambdaType(leaf); - const typeName: string | undefined = builderLambdaTypeName(leaf); - const args: (arkts.AstNode | undefined)[] = [ - builderLambdaFactory.createStyleArgInBuilderLambda(lambdaBody, typeNode), - ]; - let index = 0; - while (index < params.length) { - const isReusable: boolean = typeName - ? arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName).isReusable - : false; - if (isReusable && index === params.length - 1) { - const reuseId = arkts.factory.createStringLiteral(typeName!); - args.push(createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), isExternal, typeName, reuseId)); - } else { - args.push(createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), isExternal, typeName)); - } - index++; - } - return args; -} - -function createOrUpdateArgInBuilderLambda( - arg: arkts.Expression | undefined, - isExternal?: boolean, - typeName?: string, - fallback?: arkts.AstNode -): arkts.AstNode { - if (!arg) { - return fallback ?? arkts.factory.createUndefinedLiteral(); - } - if (arkts.isArrowFunctionExpression(arg)) { - return processArgArrowFunction(arg, isExternal); - } - if (arkts.isTSAsExpression(arg)) { - return processArgTSAsExpression(arg, typeName!); - } - return arg; -} - -function processArgArrowFunction( - arg: arkts.ArrowFunctionExpression, - isExternal?: 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) => updateContentBodyInBuilderLambda(st, isExternal)) - ) - : undefined, - arkts.FunctionSignature.createFunctionSignature(func.typeParams, func.params, func.returnTypeAnnotation, false), - func.flags, - func.modifiers - ); - return arkts.factory.updateArrowFunction(arg, updateFunc); -} - -function processArgTSAsExpression(arg: arkts.TSAsExpression, typeName: string): arkts.TSAsExpression { - if (!arg.expr || !arkts.isObjectExpression(arg.expr)) { - return arg; - } - const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(typeName!); - const properties = arg.expr.properties as arkts.Property[]; - properties.forEach((prop, index) => { - updateParameterPassing(prop, index, currentStructInfo, properties); - }); - const updatedExpr: arkts.ObjectExpression = arkts.ObjectExpression.updateObjectExpression( - arg.expr, - arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - properties, - false - ); - return arkts.TSAsExpression.updateTSAsExpression(arg, updatedExpr, arg.typeAnnotation, arg.isConst); -} - -function updateContentBodyInBuilderLambda(statement: arkts.Statement, isExternal?: boolean): arkts.Statement { - if ( - arkts.isExpressionStatement(statement) && - arkts.isCallExpression(statement.expression) && - isBuilderLambda(statement.expression, isExternal) - ) { - return arkts.factory.updateExpressionStatement(statement, transformBuilderLambda(statement.expression)); - } - // TODO: very time-consuming... - if (arkts.isIfStatement(statement)) { - return updateIfElseContentBodyInBuilderLambda(statement, isExternal); - } - - return statement; -} - -function updateParameterPassing( - prop: arkts.Property, - index: number, - currentStructInfo: arkts.StructInfo, - properties: arkts.Property[] -): void { - if ( - prop.key && - prop.value && - 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.properties.includes(DecoratorNames.LINK)) { - properties[index] = arkts.Property.updateProperty( - prop, - arkts.factory.createIdentifier(backingField(prop.key.name)), - builderLambdaFactory.updateBackingMember(prop.value, prop.value.property.name) - ); - } - } -} - -// TODO: very time-consuming... -function updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode, isExternal?: boolean): arkts.AstNode { - if (arkts.isIfStatement(statement)) { - const alternate = !!statement.alternate - ? updateIfElseContentBodyInBuilderLambda(statement.alternate, isExternal) - : statement.alternate; - const consequence = updateIfElseContentBodyInBuilderLambda(statement.consequent, isExternal); - return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); - } - if (arkts.isBlockStatement(statement)) { - return arkts.factory.updateBlock( - statement, - statement.statements.map((st) => updateContentBodyInBuilderLambda(st, isExternal)) - ); - } - return statement; } diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index 5208f9995cba2fb2668c6ff02490f36c32ebc797..8d9c5aaf41c0fcb5d391b2acd908fd664a5479e9 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -21,49 +21,76 @@ import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; import { CustomComponentNames, getCustomComponentOptionsName, - createOptionalClassProperty, findLocalImport, + CustomComponentInfo, + collectCustomComponentScopeInfo, + isComponentStruct, } from './utils'; -import { isAnnotation, updateStructMetadata, backingField, expectName, annotation } from '../common/arkts-utils'; -import { EntryWrapperNames, findEntryWithStorageInClassAnnotations } from './entry-translators/utils'; +import { + 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 { - hasDecorator, + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + DecoratorIntrinsicNames, DecoratorNames, - getStateManagementType, - collectPropertyDecorators, -} from './property-translators/utils'; -import { factory } from './ui-factory'; + DECORATOR_TYPE_MAP, + ENTRY_POINT_IMPORT_SOURCE_NAME, + NavigationNames, + EntryWrapperNames, +} from '../common/predefines'; +import { generateInstantiateInterop } from './interop/interop'; +import { createCustomDialogMethod, isNewCustomDialogController, transformController } from './customdialog'; export interface ComponentTransformerOptions extends VisitorOptions { arkui?: string; + projectConfig?: ProjectConfig; } -type ScopeInfo = { - name: string; - isEntry?: boolean; - isComponent?: boolean; - isReusable?: boolean; -}; +type ScopeInfo = CustomComponentInfo; -interface ComponentContext { - structMembers: Map; +export interface InteropContext { + className: string; + path: string; + line?: number; + col?: number; + arguments?: arkts.ObjectExpression; } 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 isEntryPointImported: boolean = 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 customDialogController: string = ''; + private projectConfig: ProjectConfig | undefined; + private entryRouteName: string | undefined; constructor(options?: ComponentTransformerOptions) { const _options: ComponentTransformerOptions = options ?? {}; super(_options); - this.arkui = _options.arkui; + this.projectConfig = options?.projectConfig; } reset(): void { @@ -71,36 +98,64 @@ 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.isEntryPointImported = false; + this.isPageLifeCycleImported = false; + this.isLayoutCallbackImported = false; + this.shouldAddLinkIntrinsic = false; + this.hasLegacy = false; + this.legacyStructMap = new Map(); + this.legacyCallMap = new Map(); + this.customDialogController = ''; } 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.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 + ); + } + if (this.isCustomDialog() && arkts.isClassProperty(node)) { + this.hasCustomDialogController(node); + } } exit(node: arkts.AstNode) { @@ -112,22 +167,46 @@ export class ComponentTransformer extends AbstractVisitor { } } - isComponentStruct(): boolean { - if (this.scopeInfos.length === 0) return false; + isCustomDialog(): boolean { + if (this.scopeInfos.length === 0) { + return false; + } const scopeInfo: ScopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; - return !!scopeInfo.isComponent; + return !!scopeInfo.annotations.customdialog; } - 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); + hasController(node: arkts.ETSTypeReference, key_name: string): void { + const ident = node.part?.name; + if (ident && arkts.isIdentifier(ident) && ident.name === 'CustomDialogController') { + this.customDialogController = key_name; + } + } + + hasCustomDialogController(node: arkts.ClassProperty): void { + const key = node.key; + 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)) { + this.hasController(type, key.name); + } + } + } else if (arkts.isETSTypeReference(typeAnno)) { + this.hasController(typeAnno, key.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, @@ -137,20 +216,50 @@ 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(); + if (!this.isCustomComponentImported) + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_CLASS_NAME + ); + if (!this.isCustomComponentV2Imported) + this.createImportDeclaration( + CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, + CustomComponentNames.COMPONENT_V2_CLASS_NAME + ); + if (!this.isLayoutCallbackImported) + this.createImportDeclaration(CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, CustomComponentNames.LAYOUT_CALLBACK); updateStatements.push(...this.componentInterfaceCollection); } 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]); @@ -158,38 +267,59 @@ export class ComponentTransformer extends AbstractVisitor { return node; } + 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 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); - 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), - arkts.factory.createFunctionExpression(script), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, - false - ); + return factory.createMethodDefinition({ + key, + kind, + function: { + key, + body, + params: [param], + returnTypeAnnotation, + flags, + modifiers, + }, + modifiers, + }); } processComponent( @@ -200,31 +330,30 @@ 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; } } + if (!!scopeInfo.annotations.customdialog) { + const setDialogController = createCustomDialogMethod(this.customDialogController); + newDefinitionBody.push(setDialogController); + this.customDialogController = ''; + } const newDefinition: arkts.ClassDefinition = this.createNewDefinition( node, className, @@ -233,8 +362,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); @@ -247,112 +379,194 @@ export class ComponentTransformer extends AbstractVisitor { definition: arkts.ClassDefinition, newDefinitionBody: arkts.AstNode[] ): arkts.ClassDefinition { - const staticMethonBody: arkts.AstNode[] = []; + const staticMethodBody: arkts.AstNode[] = []; const hasExportFlag = (node.modifiers & arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT) === arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; if (hasExportFlag) { const buildCompatibleNode: arkts.MethodDefinition = this.createStaticMethod(definition); if (!!buildCompatibleNode) { - staticMethonBody.push(buildCompatibleNode); + 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, ...staticMethonBody], - definition.modifiers, - arkts.classDefinitionFlags(definition) | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_FINAL - ); + const scopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; + const extendsName: string = scopeInfo.annotations.component + ? CustomComponentNames.COMPONENT_CLASS_NAME + : CustomComponentNames.COMPONENT_V2_CLASS_NAME; + return arkts.factory + .createClassDefinition( + definition.ident, + undefined, + undefined, // superTypeParams doen't work + [...definition.implements, ...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)) { - 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( - newName, - member, - getStateManagementType(member), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - return [originMember, newMember]; + 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]; } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - originMember.setAnnotations([annotation('memo')]); + 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, + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ) + .setAnnotations([targetInfo.annotation.clone()]); + if (isDecoratorAnnotation(targetInfo.annotation, DecoratorNames.LINK, true)) { + this.shouldAddLinkIntrinsic = true; + originMember.setAnnotations([annotation(DecoratorIntrinsicNames.LINK)]); + } + return [originMember, newMember]; } return [originMember]; } + registerMap(map: Map): void { + this.legacyStructMap = map; + this.hasLegacy = true; + } + + processInteropImport(node: arkts.ETSImportDeclaration): void { + const source = node.source?.str!; + 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 as arkts.ImportSpecifier)!.local!.name; + if (structMap[name]) { + this.legacyCallMap.set(name, structMap[name]); + } + } + } + } + + processInteropCall(node: arkts.CallExpression): arkts.CallExpression { + const ident = node.expression; + if (!(ident instanceof arkts.Identifier)) { + return node; + } + const className = ident.name; + 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, + }; + 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 (isNewCustomDialogController(newNode)) { + return transformController(newNode as arkts.ETSNewClassInstanceExpression); + } + 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); + } + // process interop code + if (!this.hasLegacy) { + return newNode; + } + if (arkts.isETSImportDeclaration(newNode)) { + this.processInteropImport(newNode); + } + if (arkts.isCallExpression(newNode)) { + return this.processInteropCall(newNode); + } return newNode; } } diff --git a/arkui-plugins/ui-plugins/customdialog.ts b/arkui-plugins/ui-plugins/customdialog.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a238ab053c2c1e80b9fb0eab0e7599cacb46ea2 --- /dev/null +++ b/arkui-plugins/ui-plugins/customdialog.ts @@ -0,0 +1,468 @@ +/* + * 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 './ui-factory'; +import { + CustomComponentNames, + getCustomComponentOptionsName, +} from './utils'; +import { stat } from 'fs'; +import { createAndInsertImportDeclaration } from '../common/arkts-utils'; + +export function createCustomDialogMethod(controller: string): arkts.MethodDefinition { + const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'controller', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(CustomComponentNames.CUSTOMDIALOG_CONTROLLER) + ) + ) + ), + undefined + ); + + const block = arkts.factory.createBlock( + (controller.length !== 0) ? [ + arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(controller), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier('controller') + ) + ) + ] : [] + ); + + const script = arkts.factory.createScriptFunction( + block, + 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 + ); + + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(CustomComponentNames.SETDIALOGCONTROLLER_METHOD), + script, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); +} + +export function transformCallToArrow(value: arkts.CallExpression): arkts.ArrowFunctionExpression { + const className = value.expression.name; + const args = value.arguments; + const as_value = arkts.factory.createExpressionStatement( + arkts.factory.updateCallExpression( + value, + value.expression, + value.typeArguments, + args.length === 0 ? [] : [ + arkts.factory.createTSAsExpression( + args[0], + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(getCustomComponentOptionsName(className)) + ) + ), + false + ) + ] + ) + ); + const newValue = arkts.factory.createArrowFunction( + factory.createScriptFunction( + { + body: arkts.factory.createBlock([as_value]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + } + ) + ); + return newValue; +} + +export function transformController(newInstance: arkts.ETSNewClassInstanceExpression): arkts.ETSNewClassInstanceExpression { + const arg = newInstance.getArguments[0]; + if (!arkts.isObjectExpression(arg)) { + throw new Error('Error CustomDialogOptions'); + } + const properties = arg.properties as arkts.Property[]; + const property = properties[0]; + const value = property?.value; + if (!(value && arkts.isCallExpression(value) && arkts.isIdentifier(value.expression))) { + return newInstance; + } + + const memoArrow = transformCallToArrow(value); + properties[0] = arkts.Property.updateProperty( + property, + property.key, + memoArrow + ); + const newObj = arkts.ObjectExpression.updateObjectExpression( + arg, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + properties, + false + ); + const asOptions = arkts.factory.createTSAsExpression( + newObj, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(CustomComponentNames.CUSTOMDIALOG_CONTROLLER_OPTIONS) + ) + ), + false + ); + return arkts.factory.updateETSNewClassInstanceExpression( + newInstance, + newInstance.getTypeRef, + [asOptions] + ); +} + +function createVarExpression(key_name: string, isProperty: boolean): arkts.Expression { + if (!isProperty) { + return arkts.factory.createIdentifier(key_name + '_Temp'); + } + return arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(key_name), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); +} + +function createInvoke(key_name: string, isProperty: boolean): arkts.AstNode[] { + const statements: arkts.AstNode[] = []; + const varExpression = createVarExpression(key_name, isProperty); + if (!isProperty) { + const createVar = arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + arkts.factory.createIdentifier((varExpression as arkts.Identifier).name), + arkts.factory.createIdentifier(key_name) + ) + ] + ); + statements.push(createVar); + } + const invoke = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('newInstance'), + arkts.factory.createIdentifier(CustomComponentNames.SETDIALOGCONTROLLER_METHOD), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createTSAsExpression( + varExpression, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(CustomComponentNames.CUSTOMDIALOG_CONTROLLER) + ) + ), + false + ) + ] + ) + ); + statements.push(invoke); + return statements; +} + +function updateStyleBlock(key_name: string, dialogName: string, isProperty: boolean): arkts.BlockStatement { + const invokeSetController = createInvoke(key_name, isProperty); + return arkts.factory.createBlock( + [ + 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('newInstance'), + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(dialogName)) + ), + [] + ) + ) + ] + ), + ...invokeSetController, + arkts.factory.createReturnStatement( + arkts.factory.createIdentifier('newInstance') + ) + ] + ); +} + +function updateStyle(style: arkts.ArrowFunctionExpression, key_name: string, dialogName: string, isProperty: boolean): arkts.ArrowFunctionExpression { + const block = updateStyleBlock(key_name, dialogName, isProperty); + return arkts.factory.updateArrowFunction( + style, + factory.createScriptFunction( + { + body: block, + returnTypeAnnotation: arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(dialogName) + ) + ), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + } + ) + ); +} + +export function updateArrow(arrow: arkts.ArrowFunctionExpression, controller: string, isProperty: boolean): arkts.ArrowFunctionExpression { + const scriptFunction = arrow.scriptFunction as arkts.ScriptFunction; + const statement = scriptFunction.body!.statements[0] as arkts.ExpressionStatement; + const call = statement.expression as arkts.CallExpression; + const member = call.expression as arkts.MemberExpression; + + const dialogName = member.object.name; + const styleArrow = call.arguments[1] as arkts.ArrowFunctionExpression; + const newStyle = updateStyle(styleArrow, controller, dialogName, isProperty); + const newScriptFunction = factory.createScriptFunction( + { + body: arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + arkts.factory.updateCallExpression( + call, + member, + call.typeArguments, + [ + call.arguments[0], + newStyle, + ...call.arguments.slice(2) + ] + ) + ) + ]), + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + } + ); + const newArrow = arkts.factory.updateArrowFunction( + arrow, + newScriptFunction + ); + return newArrow; +} + +export function updateCtor(ctor: arkts.MethodDefinition): arkts.MethodDefinition { + const script = ctor.scriptFunction; + const newScriptFunction = arkts.factory.createScriptFunction( + script.body, + arkts.factory.createFunctionSignature( + undefined, + [ + ...script.params, + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'component', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('ExtendableComponent') + ) + ) + ), + undefined + ) + ], + undefined, + false + ), + script.flags, + script.modifiers + ); + const newCtor = arkts.factory.updateMethodDefinition( + ctor, + ctor.kind, + arkts.factory.createIdentifier(ctor.name.name), + newScriptFunction, + ctor.modifiers, + false + ); + return newCtor; +} + +export function updateBody(body: arkts.Statement[]): arkts.Statement[] { + let result: arkts.Statement[] = []; + for (const statement of body) { + if (arkts.isMethodDefinition(statement) && statement.name.name === 'constructor') { + const ctor = updateCtor(statement); + result.push(ctor); + } else { + result.push(statement); + } + } + return result; +} + + +export function insertImportDeclaration(program: arkts.Program | undefined): void { + if (!program) { + throw Error('Failed to insert import: Transformer has no program'); + } + const imported = arkts.factory.createIdentifier('ExtendableComponent'); + createAndInsertImportDeclaration( + arkts.factory.createStringLiteral('./extendableComponent'), + imported, + imported, + arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, + program + ); +} + +export function transformDeclaration(node: arkts.ClassDeclaration): arkts.ClassDeclaration { + const definition = node.definition!; + const newBody = updateBody(definition.body as arkts.Statement[]); + const newDefinition = arkts.factory.updateClassDefinition( + definition, + definition?.ident, + undefined, + definition.superTypeParams, + definition.implements, + undefined, + definition.super, + newBody, + definition.modifiers, + arkts.classDefinitionFlags(definition) + ); + const declaration = arkts.factory.updateClassDeclaration( + node, + newDefinition + ); + return declaration; +} + +export function updateNewClassInstanceExpression(node: arkts.ETSNewClassInstanceExpression, varName: string, + isProperty: boolean): arkts.ETSNewClassInstanceExpression { + const asExression = node.getArguments[0] as arkts.TSAsExpression; + const arg = asExression.expr as arkts.ObjectExpression; + if (!arkts.isObjectExpression(arg)) { + throw new Error('Error CustomDialogOptions'); + } + const properties = arg.properties as arkts.Property[]; + const builder = properties[0]; + const builder_value = builder.value as arkts.ArrowFunctionExpression; + const newBuilderValue = updateArrow(builder_value, varName, isProperty); + const newProperty = arkts.factory.updateProperty( + builder, + builder.key, + newBuilderValue + ); + const newObj = arkts.factory.updateObjectExpression( + arg, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [newProperty, ...properties.slice(1)], + false + ); + const newAsExpression = arkts.factory.updateTSAsExpression( + asExression, + newObj, + asExression.typeAnnotation, + asExression.isConst + ); + const typeRef = node.getTypeRef as arkts.ETSTypeReference; + const newNode = arkts.factory.updateETSNewClassInstanceExpression( + node, + typeRef, + [newAsExpression, arkts.factory.createThisExpression()] + ); + return newNode; +} + +export function isNewCustomDialogController(node: arkts.AstNode | undefined): boolean { + if (node && arkts.isETSNewClassInstanceExpression(node) && + node.getTypeRef?.part?.name.name === 'CustomDialogController') { + return true; + } + return false; +} + +function updateVar(node: arkts.VariableDeclarator): arkts.VariableDeclarator { + if (node.flag !== arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET) { + throw Error('Error VariableDeclarator CustomDialogController'); + } + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + node.name, + arkts.factory.createUndefinedLiteral() + ); +} + +export function checkCustomDialogController(node: arkts.BlockStatement): arkts.BlockStatement { + const statements = node.statements; + const newStatements: arkts.AstNode[] = []; + for (let i = 0; i < statements.length; i++) { + const statement = statements[i]; + if (arkts.isVariableDeclaration(statement) && statement.declarators.length > 0 && + isNewCustomDialogController(statement.declarators[0].initializer)) { + const varDeclare = statement.declarators[0]; + const varName = varDeclare.name.name; + const classInstance = varDeclare.initializer; + const newClass = updateNewClassInstanceExpression(classInstance as arkts.ETSNewClassInstanceExpression, varName, false); + const newVar = arkts.factory.updateVariableDeclaration( + statement, + 0, + statement.declarationKind, + [updateVar(statement.declarators[0])] + ); + newStatements.push(newVar); + const initVar = arkts.factory.createAssignmentExpression( + arkts.factory.createIdentifier(varName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + newClass + ); + const initStatement = arkts.factory.createExpressionStatement(initVar); + newStatements.push(initStatement); + } else { + newStatements.push(statement); + } + } + return arkts.factory.updateBlock( + node, + newStatements + ); + +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index e2d04b3d2bdc1ce47beda8c1878c02ccbb353d59..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]); @@ -108,7 +105,7 @@ export class factory { const def = arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, key, - arkts.factory.createFunctionExpression(entryScript), + entryScript, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); @@ -132,7 +129,7 @@ export class factory { const def = arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, key, - arkts.factory.createFunctionExpression(entryScript), + entryScript, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, false ); @@ -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 c3b1dfbefc1fe994af7337fdf5f5ad687dc588e8..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 = '@ohos.arkui.component', - 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 76e2e6883f9e35619ed13126345036557710205e..1785e4854352aa9d54977fe12a7ed1909928947e 100644 --- a/arkui-plugins/ui-plugins/index.ts +++ b/arkui-plugins/ui-plugins/index.ts @@ -15,9 +15,8 @@ import * as arkts from '@koalaui/libarkts'; import { ComponentTransformer } from './component-transformer'; -import { PreprocessorTransformer } from './preprocessor-transform'; import { CheckedTransformer } from './checked-transformer'; -import { Plugins, PluginContext } from '../common/plugin-context'; +import { Plugins, PluginContext, ProjectConfig } from '../common/plugin-context'; import { ProgramVisitor } from '../common/program-visitor'; import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../common/predefines'; import { debugDump, debugLog, getDumpFileName } from '../common/debug'; @@ -36,30 +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'); - const componentTransformer = new ComponentTransformer(); - const preprocessorTransformer = new PreprocessorTransformer(); - const programVisitor = new ProgramVisitor({ - pluginName: uiTransform.name, - state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, - visitors: [componentTransformer, preprocessorTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, - pluginContext: this, - }); - program = programVisitor.programVisitor(program); + program = parsedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-parsed', true); debugLog('[AFTER PARSED SCRIPT] script: ', script.dumpSrc()); @@ -68,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,32 +75,53 @@ function parsedTransform(this: PluginContext): arkts.EtsScript | undefined { return script; } +function parsedProgramVisit( + program: arkts.Program, + context: PluginContext, + canSkipPhases: boolean = false +): arkts.Program { + if (canSkipPhases) { + debugLog('[SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); + } else { + debugLog('[CANT SKIP PHASE] phase: ui-parsed, moduleName: ', program.moduleName); + const componentTransformer = new ComponentTransformer({ + projectConfig: context.getProjectConfig(), + }); + const programVisitor = new ProgramVisitor({ + pluginName: uiTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + visitors: [componentTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: context, + }); + program = programVisitor.programVisitor(program); + } + return program; +} + 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'); - const checkedTransformer = new CheckedTransformer(this.getProjectConfig()); - const programVisitor = new ProgramVisitor({ - pluginName: uiTransform.name, - state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, - visitors: [checkedTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, - pluginContext: this, - }); - program = programVisitor.programVisitor(program); + program = checkedProgramVisit(program, this, canSkipPhases); script = program.astNode; arkts.Performance.getInstance().stopEvent('ui-checked', true); debugLog('[AFTER STRUCT SCRIPT] script: ', script.dumpSrc()); @@ -112,17 +130,40 @@ 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; } console.log('[UI PLUGIN] AFTER CHECKED EXIT WITH NO TRANSFORM'); return script; } + +function checkedProgramVisit( + program: arkts.Program, + context: PluginContext, + canSkipPhases: boolean = false +): arkts.Program { + if (canSkipPhases) { + debugLog('[SKIP PHASE] phase: ui-checked, moduleName: ', program.moduleName); + } 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, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [checkedTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: context, + }); + program = programVisitor.programVisitor(program); + } + return program; +} diff --git a/arkui-plugins/ui-plugins/interop/initstatevar.ts b/arkui-plugins/ui-plugins/interop/initstatevar.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ac0d576bdea91864434c737c86d267b3a6822f1 --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/initstatevar.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 { InteroperAbilityNames } from './predefines'; +import { annotation, backingField, isAnnotation } from '../../common/arkts-utils'; +import { stateProxy, getWrapValue, setPropertyESValue } 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)) { + 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 { + throw Error('Unsupported decorators.'); + } + } + 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(), + arkts.factory.createIdentifier(InteroperAbilityNames.CREATESTATE) + ] + ) + ); +} + +/** + * + * @param keyName + * @param value + * @param type + * @param proxySet + * @returns generate code to process @Link data interoperability + */ +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 (hasDecorator(valueDecl, DecoratorNames.STATE) || hasDecorator(valueDecl, DecoratorNames.PROP) || + hasDecorator(valueDecl, DecoratorNames.PROVIDE) || hasDecorator(valueDecl, DecoratorNames.LINK) || + hasDecorator(valueDecl, DecoratorNames.CONSUME)) { + if (!proxySet.has(varName)) { + proxySet.add(varName); + const getProxy = getStateProxy(proxyName, stateVar); + result.push(getProxy); + } + } else { + throw Error('unsupported decorator for Link'); + } + const setParam = setPropertyESValue( + 'param', + keyName, + arkts.factory.createIdentifier(proxyName) + ); + result.push(setParam); + } else { + throw Error('unsupported data for Link'); + } + return result; +} + +/** + * + * @param keyName + * @param value + * @returns generate code to process regular data interoperability + */ +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; +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/interop.ts b/arkui-plugins/ui-plugins/interop/interop.ts new file mode 100644 index 0000000000000000000000000000000000000000..c39a6417561bba0dd66571af3ef0d57d1a2f78ca --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/interop.ts @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; +import { getCustomComponentOptionsName } from '../utils'; +import { InteropContext } from '../component-transformer'; +import { createVariableLet, initialArgs} from './initstatevar'; +import { createProvideInterop, setAndResetFindProvide } from './provide'; +import { getPropertyESValue, getWrapValue, setPropertyESValue, createEmptyESValue } from './utils'; +import { ImportCollector } from '../../common/import-collector'; + + +function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): arkts.Statement[] { + const result: arkts.Statement[] = []; + result.push( + 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.PARAMSLAMBDA), + arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock([arkts.factory.createReturnStatement( + args ? args : arkts.ObjectExpression.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [], + false + ), + )]), + arkts.factory.createFunctionSignature( + undefined, + [], + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(getCustomComponentOptionsName(name)) + ) + ), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ) + ), + + ] + ) + ); + 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)); + properties.forEach((prop, index) => { + const val = value[index]; + body.push(setPropertyESValue( + InteroperAbilityNames.EXTRAINFO, + prop, + arkts.factory.createStringLiteral(val)) + ); + }); + 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(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 + ) + )] + ); +} + +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; +} + + +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') + ) + ), + false + ); +} + +function newComponent(className: string): arkts.Statement { + return createVariableLet( + InteroperAbilityNames.COMPONENT, + getWrapValue( + arkts.factory.createETSNewClassInstanceExpression( + arkts.factory.createIdentifier(className), + [ + generateTSASExpression(getWrapValue(arkts.factory.createUndefinedLiteral())), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.PARAM)), + generateTSASExpression(getWrapValue(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 + ), + generateTSASExpression(arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO)) + ] + ) + ) + ); +} + +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('viewPUCreate'), + arkts.factory.createIdentifier('invoke'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ] + ) + ); + return [component, ViewPU, create]; +} + + +function createWrapperBlock(context: InteropContext, varMap: Map, + updateProp: arkts.Property[]): arkts.BlockStatement { + const enableStateManagementInterop = false; + 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 initial = [ + createGlobal(), + createEmptyESValue(InteroperAbilityNames.PARAM), + ...(enableStateManagementInterop ? createProvideInterop() : []) + ]; + const initialArgsStatement = args ? initialArgs(args, varMap, updateProp) : []; + return arkts.factory.createBlock( + [ + ...initial, + ...initialArgsStatement, + ...createExtraInfo(['page'], [path]), + ...createELMTID(), + ...createComponent(className), + ...(enableStateManagementInterop ? setAndResetFindProvide() : []), + createInitReturn(className) + ] + ); +} + +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, + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +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') + ] + ) + ]; +} + +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, + 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 generateVarMap(node: arkts.Identifier): Map { + const decl = arkts.getDecl(node); + if (!(decl instanceof arkts.ClassDefinition)) { + throw Error("can't find legacy class declaration"); + } + 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; + result.set(key, node); + } + }); + return result; +} + +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() + ]; + return result; + +} + +/** + * + * @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), + arkts.factory.createIdentifier('instantiate_Interop'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + generateStructInfo(context) + ); +} + +/** + * + * @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.BINDPROVIDEINTEROP, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.BINDPROVIDEINTEROP); + ImportCollector.getInstance().collectSource(InteroperAbilityNames.GETCOMPATIBLESTATE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.GETCOMPATIBLESTATE); + 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 context: InteropContext = { + className: className, + path: path, + line: line, + col: col, + arguments: options + }; + + const varMap: Map = generateVarMap(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.NodeCache.getInstance().collect(result); + return result; +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..14a7a4b95546750144f8b9cb8d0e5f2aa8bc40d8 --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { getInteropPath } from '../../path'; +const interop = require(getInteropPath()); +const nullptr = interop.nullptr; +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 ?? {}; + super(_options); + this.structList = _options.structList ?? []; + } + + // TODO: check reset + reset(): void { + super.reset(); + this.structList = []; + this.componentInterfaceCollection = []; + this.scopeInfos = []; + } + + getList(): string[] { + return this.structList; + } + + createParam(name: string, type: string): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + name, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(type) + ) + ) + ), + undefined + ); + } + + createInteropMethod(name: string): arkts.MethodDefinition { + const path = this.createParam('path', 'string'); + const line = this.createParam('line', 'number'); + line.setOptional(true); + const col = this.createParam('col', 'number'); + col.setOptional(true); + const options = this.createParam('options', getCustomComponentOptionsName(name)); + options.setOptional(true); + + const script = arkts.factory.createScriptFunction( + arkts.factory.createBlock([]), + arkts.FunctionSignature.createFunctionSignature( + undefined, + [path, line, col, options], + 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('instantiate_Interop'), + script, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + false + ); + } + + generateMember(map: Map): arkts.ClassProperty[] { + const properties: arkts.ClassProperty[] = []; + + map.forEach((value, key) => { + const property = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(key), + undefined, + value, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL, + false + ); + + properties.push(property); + }); + + return properties; + } + + generateComponentInterface(name: string, modifiers: number, map: Map): arkts.TSInterfaceDeclaration { + const interfaceNode = arkts.factory.createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(getCustomComponentOptionsName(name)), + nullptr, + arkts.factory.createInterfaceBody([...(this.generateMember(map) || [])]), + false, + false + ); + interfaceNode.modifiers = modifiers; + return interfaceNode; + } + + processComponent(node: arkts.StructDeclaration): arkts.StructDeclaration | arkts.ClassDeclaration { + const definition: arkts.ClassDefinition = node.definition!; + const ident = definition.ident!; + const hasExportFlag = + (node.modifiers & arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT) === + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + if (hasExportFlag) { + this.structList.push(ident.name); + } + + const instantiate_Interop: arkts.MethodDefinition = this.createInteropMethod(ident.name); + + const newDefinition = arkts.factory.updateClassDefinition( + definition, + definition.ident, + definition.typeParams, + definition.superTypeParams, + definition.implements, + undefined, + definition.super, + [...definition.body, instantiate_Interop], + definition.modifiers, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + + if (arkts.isStructDeclaration(node)) { + const _node = arkts.factory.createClassDeclaration(newDefinition); + _node.modifiers = node.modifiers; + return _node; + } else { + return arkts.factory.updateClassDeclaration(node, newDefinition); + } + } + + processConstructor(node: arkts.MethodDefinition): arkts.MethodDefinition { + 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, + node.name, + script, + node.modifiers, + false + ); + } + + collectComponentMembers(node: arkts.StructDeclaration, className: string): Map { + const result: Map = new Map(); + node.definition.body.map((it) => { + if (arkts.isClassProperty(it)) { + const name = (it.key as arkts.Identifier).name; + const type = it.typeAnnotation!; + result.set(name, type); + } + }); + return result; + } + + processEtsScript(node: arkts.EtsScript): arkts.EtsScript { + let updateStatements: arkts.AstNode[] = []; + if (this.componentInterfaceCollection.length > 0) { + updateStatements.push(...this.componentInterfaceCollection); + } + if (updateStatements.length > 0) { + return arkts.factory.updateEtsScript(node, [...node.statements, ...updateStatements]); + } + 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(); + } + } + } + + 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 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 (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; + } + } + 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..6762902a09264db690a251e9d718052f46e6acd7 --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/predefines.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. + */ + + +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', + BINDPROVIDEINTEROP = 'bindCompatibleProvideCallback', + 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', +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/provide.ts b/arkui-plugins/ui-plugins/interop/provide.ts new file mode 100644 index 0000000000000000000000000000000000000000..02536edebd6d00f471ac8f5f1657e084204c7e9f --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/provide.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { getPropertyESValue, getWrapValue } from './utils'; +import { ESValueMethodNames, InteroperAbilityNames, InteropProvideNames } from './predefines'; + +export function createProvideInterop(): arkts.Statement[] { + const createState = getPropertyESValue(InteroperAbilityNames.CREATESTATE, InteroperAbilityNames.GLOBAL, InteroperAbilityNames.CREATESTATE); + const setCallbackFunc = getPropertyESValue( + InteropProvideNames.SETFINDPROVIDE, + InteroperAbilityNames.GLOBAL, + InteropProvideNames.SETFINDPROVIDE + ); + const viewPUResetFunc = getPropertyESValue( + 'resetViewPUFindProvideInterop', + InteroperAbilityNames.GLOBAL, + 'resetViewPUFindProvideInterop' + ); + const invokeFunc = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.BINDPROVIDEINTEROP), + undefined, + [ + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(InteroperAbilityNames.CREATESTATE), + arkts.factory.createIdentifier(InteropProvideNames.SETFINDPROVIDE) + ] + ) + ); + return [createState, setCallbackFunc, viewPUResetFunc, invokeFunc]; +} + + +export function setAndResetFindProvide(): arkts.Statement[] { + const setComponent = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(InteroperAbilityNames.BINDPROVIDEINTEROP), + undefined, + [ + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(InteroperAbilityNames.CREATESTATE), + arkts.factory.createIdentifier(InteropProvideNames.SETFINDPROVIDE), + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), + ] + ) + ); + + const resetViewPU = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('resetViewPUFindProvideInterop'), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); + return [setComponent, resetViewPU]; +} \ 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..14574a264b19bbab41047aca2fc6ff5b3ff3daec --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/utils.ts @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 } 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 + ] + ) + ); +} + +/** + * + * @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}`; +} + diff --git a/arkui-plugins/ui-plugins/preprocessor-transform.ts b/arkui-plugins/ui-plugins/preprocessor-transform.ts deleted file mode 100644 index 394b2327b021450e46ddf5384ecf9ff1a13dfc9b..0000000000000000000000000000000000000000 --- a/arkui-plugins/ui-plugins/preprocessor-transform.ts +++ /dev/null @@ -1,257 +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 } 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; - } - - 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 (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)]) - .setTralingBlock(node.trailingBlock); - } 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; - 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) && this.isExternal === false) { - 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.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) { - const newDependencies = [`${importName}Attribute`]; - this.updateOutDependencyMap(importName, newDependencies); - 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 - ); - 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..a553aaa7f79d95ae4714a6235e5d69d0701cbdb9 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 } from '../utils'; +import { StateManagementTypes } from '../../common/predefines'; -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; - 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; } + abstract translateMember(): arkts.AstNode[]; + translateGetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, @@ -77,10 +52,36 @@ 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 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 f6b08b836a46b44cb1a9f4935a89c92312e3506d..41c616f607c7c69f403d5c3db8862a4f7a5e67b8 100644 --- a/arkui-plugins/ui-plugins/property-translators/builderParam.ts +++ b/arkui-plugins/ui-plugins/property-translators/builderParam.ts @@ -15,57 +15,52 @@ 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, PropertyCache } 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'; 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); - // const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - // currentStructInfo.updateBody.push(updateStruct); - 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( + const propertyType = this.property.typeAnnotation; + if (!!propertyType && (arkts.isETSFunctionType(propertyType) || arkts.isETSUnionType(propertyType))) { + addMemoAnnotation(propertyType); + } + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - '', - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + true ); - const thisGetValue: arkts.Expression = generateThisBacking(newName, false, 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?.clone(), + 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?.clone(), thisSetValue); + arkts.NodeCache.getInstance().collect(setter); return [field, getter, setter]; } @@ -75,7 +70,7 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia typeAnnotation: arkts.TypeNode | undefined, returnValue: arkts.Expression ): arkts.MethodDefinition { - return createGetter(originalName, typeAnnotation, returnValue); + return createGetter(originalName, typeAnnotation, returnValue, true); } translateSetter( @@ -91,25 +86,86 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia return arkts.factory.createAssignmentExpression( mutableThis, arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName + arkts.factory.createBinaryExpression( + arkts.factory.createBinaryExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier('initializers'), + originalName + ), + arkts.factory.createIdentifier('content'), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + ), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING ) ); } +} - generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { - const right: arkts.MemberExpression = arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('initializers'), - arkts.factory.createIdentifier(originalName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - true - ); - return arkts.factory.createAssignmentExpression( - mutableThis, - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right - ); +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/consume.ts b/arkui-plugins/ui-plugins/property-translators/consume.ts index ac7491b708383f17314ed09e188b6e1880ad8169..8bd40ec2e46c89602fc559259a56fe61a7ea69ef 100644 --- a/arkui-plugins/ui-plugins/property-translators/consume.ts +++ b/arkui-plugins/ui-plugins/property-translators/consume.ts @@ -15,51 +15,60 @@ import * as arkts from '@koalaui/libarkts'; +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; import { + generateToRecord, createGetter, - createSetter, - generateThisBackingValue, + createSetter2, generateThisBacking, + generateGetOrSetCall, getValueInAnnotation, - DecoratorNames, + hasDecorator, + PropertyCache, } 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'; 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(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + 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 = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'MutableState', + StateManagementTypes.CONSUME_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); - const thisValue: arkts.MemberExpression = generateThisBackingValue(newName, false, true); + 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.property.typeAnnotation, - thisValue + thisGet ); const setter: arkts.MethodDefinition = this.translateSetter( originalName, this.property.typeAnnotation, - thisValue + thisSet ); return [field, getter, setter]; @@ -68,7 +77,7 @@ export class ConsumeTranslator extends PropertyTranslator implements Initializer translateGetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, - returnValue: arkts.MemberExpression + returnValue: arkts.Expression ): arkts.MethodDefinition { return createGetter(originalName, typeAnnotation, returnValue); } @@ -76,37 +85,71 @@ export class ConsumeTranslator extends PropertyTranslator implements Initializer translateSetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, - left: arkts.MemberExpression + statement: arkts.AstNode ): arkts.MethodDefinition { - const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('observableProxy'), - undefined, - [arkts.factory.createIdentifier('value')] - ); - - return createSetter(originalName, typeAnnotation, left, right); + return createSetter2(originalName, typeAnnotation, statement); } - generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - let consumeValueStr: string | undefined = getValueInAnnotation(this.property, DecoratorNames.CONSUME); - if (!consumeValueStr) { - consumeValueStr = originalName; - } - const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('contextLocal'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : undefined, - [arkts.factory.create1StringLiteral(consumeValueStr)] - ); - return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + 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, - right + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_CONSUME, + this.property.typeAnnotation, + 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/factory.ts b/arkui-plugins/ui-plugins/property-translators/factory.ts index 7125b11c78039503049da9122dea73c01df94c6c..c6bc8e859e99a776b0384e07e9d4685c5c3a3aea 100644 --- a/arkui-plugins/ui-plugins/property-translators/factory.ts +++ b/arkui-plugins/ui-plugins/property-translators/factory.ts @@ -14,11 +14,13 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getInteropPath } from '../../path'; import { GenSymGenerator } from '../../common/gensym-generator'; +import { DecoratorNames, DECORATOR_TYPE_MAP, StateManagementTypes } from '../../common/predefines'; import { factory as UIFactory } from '../ui-factory'; -const interop = require(getInteropPath()); -const nullptr = interop.nullptr; +import { collectStateManagementTypeImport, getValueInAnnotation, hasDecorator, removeDecorator } from './utils'; +import { CustomComponentNames } from '../utils'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; +import { annotation } from '../../common/arkts-utils'; export class factory { /** @@ -233,4 +235,569 @@ export class factory { args?.length ? args : [] ); } + + /* + * create `StateMgmtFactory.(this, ...);`. + */ + static generateStateMgmtFactoryCall( + makeType: StateManagementTypes, + typeArguments: arkts.TypeNode | undefined, + args: arkts.AstNode[], + argsContainsThis: boolean + ): arkts.CallExpression { + collectStateManagementTypeImport(StateManagementTypes.STATE_MANAGEMENT_FACTORY); + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.STATE_MANAGEMENT_FACTORY), + arkts.factory.createIdentifier(makeType), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + 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)), + ]) + ); + } + + 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, + stageManagementType: StateManagementTypes | undefined, + modifiers: arkts.Es2pandaModifierFlags, + needMemo: boolean = false + ): arkts.ClassProperty { + const newType: arkts.TypeNode | undefined = property.typeAnnotation?.clone(); + if (needMemo && findCanAddMemoFromTypeAnnotation(newType)) { + addMemoAnnotation(newType); + } + const newProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(name), + undefined, + !!stageManagementType ? factory.createStageManagementType(stageManagementType, property) : newType, + modifiers, + false + ); + return arkts.classPropertySetOptional(newProperty, true); + } + + static createStageManagementType( + stageManagementType: StateManagementTypes, + property: arkts.ClassProperty + ): arkts.ETSTypeReference { + collectStateManagementTypeImport(stageManagementType); + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(stageManagementType), + arkts.factory.createTSTypeParameterInstantiation([ + property.typeAnnotation ? property.typeAnnotation.clone() : 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 + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ), + 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('subscribedWatches'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.factory.createIdentifier(member), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + } + + /* + * create ____V1RenderId related members in Observed/Track classes + */ + static createV1RenderIdMembers(): arkts.AstNode[] { + const v1RenderId: arkts.ClassProperty = arkts.factory.createClassProperty( + arkts.factory.createIdentifier('____V1RenderId'), + 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(); + return [v1RenderId, setV1RenderId]; + } + + /* + * helper for createV1RenderIdMembers to generate setV1RenderId method + */ + static setV1RenderId(): arkts.MethodDefinition { + const assignRenderId: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('____V1RenderId'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier('renderId') + ) + ); + const funcSig: arkts.FunctionSignature = arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'renderId', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.RENDER_ID_TYPE) + ) + ) + ), + undefined + ), + ], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier('setV1RenderId'), + arkts.factory.createScriptFunction( + arkts.factory.createBlock([assignRenderId]), + funcSig, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /* + * create conditionalAddRef method in Observed/Track classes + */ + static conditionalAddRef(): arkts.MethodDefinition { + const funcSig: arkts.FunctionSignature = arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'meta', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.MUTABLE_STATE_META) + ) + ) + ), + undefined + ), + ], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ); + collectStateManagementTypeImport(StateManagementTypes.MUTABLE_STATE_META); + const shouldAddRef: arkts.IfStatement = factory.shouldAddRef(); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier('conditionalAddRef'), + arkts.factory.createScriptFunction( + arkts.factory.createBlock([shouldAddRef]), + funcSig, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + false + ); + } + + /* + * helper for conditionalAddRef to generate shouldAddRef method + */ + static shouldAddRef(): arkts.IfStatement { + const test: arkts.CallExpression = arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(StateManagementTypes.OBSERVE), + arkts.factory.createIdentifier('shouldAddRef'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('____V1RenderId'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ] + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVE); + const consequent: arkts.BlockStatement = arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('meta'), + arkts.factory.createIdentifier('addRef'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ), + ]); + 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), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(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, + 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(), + ] + ), + 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; + } + + /** + * 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; + } + + /** + * create `Type.from()` when translating `@StorageLink`, `@StorageProp`, `@LocalStorageLink` + * + * @param typeAnnotation expecting property's original type annotation. + */ + static createTypeFrom(typeAnnotation: arkts.TypeNode | undefined): arkts.CallExpression { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('Type'), + arkts.factory.createIdentifier('from'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + typeAnnotation ? [typeAnnotation] : undefined, + undefined + ); + } } diff --git a/arkui-plugins/ui-plugins/property-translators/index.ts b/arkui-plugins/ui-plugins/property-translators/index.ts index c30ace4fcde90e8df4e188fdeff2810f2e0ef996..aa0c767c5c50ed37625b3c6ac642f0782a8ce797 100644 --- a/arkui-plugins/ui-plugins/property-translators/index.ts +++ b/arkui-plugins/ui-plugins/property-translators/index.ts @@ -15,62 +15,119 @@ 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 { DecoratorNames } from '../../common/predefines'; +import { InterfacePropertyTranslator, 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 { isStatic } from '../utils'; -import { StoragePropTranslator } from './storageProp'; -import { ConsumeTranslator } from './consume'; -import { ProvideTranslator } from './provide'; -import { BuilderParamTranslator } from './builderParam'; +import { CustomComponentInfo, isStatic } from '../utils'; +import { StoragePropInterfaceTranslator, StoragePropTranslator } from './storageProp'; +import { ConsumeInterfaceTranslator, ConsumeTranslator } from './consume'; +import { ProvideInterfaceTranslator, ProvideTranslator } from './provide'; +import { BuilderParamInterfaceTranslator, BuilderParamTranslator } from './builderParam'; +import { ObservedTrackTranslator } from './observedTrack'; +import { ClassScopeInfo } from './types'; -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 classifyProperty( + property: arkts.AstNode, + structInfo: CustomComponentInfo +): PropertyTranslator | undefined { + if (!arkts.isClassProperty(property)) return undefined; + if (isStatic(property)) return new staticPropertyTranslator({ property, structInfo }); - if (hasDecorator(member, DecoratorNames.STATE)) { - return new StateTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STATE)) { + return new StateTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_LINK)) { - return new StorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_LINK)) { + return new StorageLinkTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_LINK)) { - return new LocalStorageLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_LINK)) { + return new LocalStorageLinkTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LINK)) { - return new LinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.LINK)) { + return new LinkTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.OBJECT_LINK)) { - return new ObjectLinkTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.OBJECT_LINK)) { + return new ObjectLinkTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.LOCAL_STORAGE_PROP)) { - return new LocalStoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP)) { + return new LocalStoragePropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.STORAGE_PROP)) { - return new StoragePropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.STORAGE_PROP)) { + return new StoragePropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROP)) { - return new PropTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROP)) { + return new PropTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.PROVIDE)) { - return new ProvideTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.PROVIDE)) { + return new ProvideTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.CONSUME)) { - return new ConsumeTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.CONSUME)) { + return new ConsumeTranslator({ property, structInfo }); } - if (hasDecorator(member, DecoratorNames.BUILDER_PARAM)) { - return new BuilderParamTranslator(member, structName); + if (hasDecorator(property, DecoratorNames.BUILDER_PARAM)) { + return new BuilderParamTranslator({ property, structInfo }); } - return new regularPropertyTranslator(member, structName); + return new RegularPropertyTranslator({ property, structInfo }); +} + +export function classifyPropertyInInterface(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 (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 (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 }); + } + if (RegularInterfaceTranslator.canBeTranslated(property)) { + return new RegularInterfaceTranslator({ property }); + } + return undefined; +} + +export function classifyObservedTrack( + member: arkts.AstNode, + classScopeInfo: ClassScopeInfo +): ObservedTrackTranslator | undefined { + if (!arkts.isClassProperty(member)) { + return undefined; + } + return new ObservedTrackTranslator(member, classScopeInfo); } diff --git a/arkui-plugins/ui-plugins/property-translators/link.ts b/arkui-plugins/ui-plugins/property-translators/link.ts index eafd90c955bfc1232e20cb6549218f525d4642e3..65217d539ea207a9cd60a0503eeea15a59d8a442 100644 --- a/arkui-plugins/ui-plugins/property-translators/link.ts +++ b/arkui-plugins/ui-plugins/property-translators/link.ts @@ -15,59 +15,72 @@ 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, + PropertyCache, } 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'; 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.property.typeAnnotation, + args, + true + ) ) ), ]); @@ -76,16 +89,16 @@ 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') + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, @@ -117,3 +130,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/localstoragelink.ts b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts index a050e985294f30dcc9ddc5b92c4bcc8673eb18da..be59f9e6b9cd3c5d4817b5f9502f72077045d063 100755 --- a/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstoragelink.ts @@ -16,20 +16,32 @@ 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'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + PropertyCache, +} from './utils'; +import { factory } from './factory'; 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 = + const isLocalStorageLinkAnnotation: boolean = !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.LOCAL_STORAGE_LINK; - if (isStorageLinkAnnotation && anno.properties.length === 1) { + if (isLocalStorageLinkAnnotation && anno.properties.length === 1) { return getLocalStorageLinkValueStr(anno.properties.at(0)!); } return undefined; @@ -45,7 +57,6 @@ function getLocalStorageLinkValueInAnnotation(node: arkts.ClassProperty): string return str; } } - return undefined; } @@ -54,55 +65,127 @@ export class LocalStorageLinkTranslator 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); - 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); 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.createTypeFrom(this.property.typeAnnotation) + ]; + 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.property.typeAnnotation, + 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.property.typeAnnotation, + thisGet ); + const setter: arkts.MethodDefinition = this.translateSetter( + originalName, + this.property.typeAnnotation, + 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..58eec31cc257158fb2b40e78e5832c5829e55e88 100644 --- a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts +++ b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts @@ -15,10 +15,17 @@ 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, + PropertyCache, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; function getLocalStorageporpValueStr(node: arkts.AstNode): string | undefined { if (!arkts.isClassProperty(node) || !node.value) return undefined; @@ -54,29 +61,63 @@ 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 + ); + + 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 + ); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + 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]; } 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,7 +127,7 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false ); const binaryItem = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('StorageLinkState'), + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), this.property.typeAnnotation ? [this.property.typeAnnotation] : [], [ insideMember, @@ -94,8 +135,9 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In this.property.value ?? arkts.factory.createUndefinedLiteral(), ] ); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); const call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('propState'), + arkts.factory.createIdentifier(StateManagementTypes.PROP_STATE), this.property.typeAnnotation ? [this.property.typeAnnotation] : [], [ arkts.factory.createMemberExpression( @@ -107,6 +149,7 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In ), ] ); + collectStateManagementTypeImport(StateManagementTypes.PROP_STATE); return arkts.factory.createAssignmentExpression( arkts.factory.createMemberExpression( arkts.factory.createThisExpression(), @@ -123,31 +166,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 +177,6 @@ export class LocalStoragePropTranslator extends PropertyTranslator implements In false, false ); - const consequent = arkts.BlockStatement.createBlockStatement([ arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( @@ -171,7 +192,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/objectlink.ts b/arkui-plugins/ui-plugins/property-translators/objectlink.ts index 49b177d4fd8eb0062a5d2aa7bfc59cff87e1009f..2c20897942a8e8ca46dac257d574c1bb5c438f45 100644 --- a/arkui-plugins/ui-plugins/property-translators/objectlink.ts +++ b/arkui-plugins/ui-plugins/property-translators/objectlink.ts @@ -15,84 +15,159 @@ import * as arkts from '@koalaui/libarkts'; -import { generateToRecord } 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, + PropertyCache, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; 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'); + } - 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 call = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('objectLinkState'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : [], - [] + const initializers = arkts.factory.createTSNonNullExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName + ) ); + const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers]; + factory.judgeIfAddWatchFunc(args, this.property); + 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_OBJECT_LINK, + this.property.typeAnnotation, + args, + true + ) ); } generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { - const test = arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + generateThisBacking(newName, false, true), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ); + const nonNullItem = arkts.factory.createTSNonNullExpression( + 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 = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.OBJECT_LINK_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.property.typeAnnotation, + thisGet + ); + return [field, getter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + 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; + } + } + return false; + } +} + +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; + } + + 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); + } - const consequent = arkts.BlockStatement.createBlockStatement([ - arkts.factory.createExpressionStatement( - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createTSNonNullExpression(test), - arkts.factory.createIdentifier('update'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - [ - factory.createBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - originalName - ), - ] - ) - ), - ]); - return arkts.factory.createExpressionStatement(arkts.factory.createIfStatement(test, consequent)); + /** + * 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 new file mode 100644 index 0000000000000000000000000000000000000000..28d0762e4cf5c86cf6941f186b4f3db1cd69ed09 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/observedTrack.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, StateManagementTypes } from '../../common/predefines'; +import { collectStateManagementTypeImport, hasDecorator, hasDecoratorName, removeDecorator } from './utils'; +import { ClassScopeInfo } from './types'; +import { factory } from './factory'; + +export class ObservedTrackTranslator { + protected property: arkts.ClassProperty; + protected classScopeInfo: ClassScopeInfo; + private hasImplement: boolean; + private isTracked: boolean; + + constructor(property: arkts.ClassProperty, classScopeInfo: ClassScopeInfo) { + this.property = property; + this.classScopeInfo = classScopeInfo; + this.hasImplement = expectName(this.property.key).startsWith(''); + 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)) + : 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.property.typeAnnotation, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + 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('newName'), + 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): arkts.MethodDefinition { + const conditionalAddRef = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('conditionalAddRef'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + this.metaIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ] + ) + ); + const returnMember: arkts.ReturnStatement = arkts.factory.createReturnStatement(this.genThisBacking(newName)); + + const body = arkts.factory.createBlock([conditionalAddRef, 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 + ); + } + + 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), + 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 + ); + } + + metaIdentifier(originalName: string): arkts.Identifier { + return this.isTracked + ? arkts.factory.createIdentifier(`${StateManagementTypes.META}_${originalName}`) + : arkts.factory.createIdentifier(StateManagementTypes.META); + } + + removeImplementProperty(originalName: string): string { + const prefix = ''; + return originalName.substring(prefix.length); + } + + 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( + (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), + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.MUTABLE_STATE_META) + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + false + ); + } + + setterIfEqualsNewValue(originalName: string, newName: string): arkts.IfStatement { + const backingValue = this.genThisBacking(newName); + + const setNewValue = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + backingValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createIdentifier('newValue') + ) + ); + + const fireChange = 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('fireChange'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); + + 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)] + ) + ); + + return arkts.factory.createIfStatement( + arkts.factory.createBinaryExpression( + backingValue, + arkts.factory.createIdentifier('newValue'), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL + ), + arkts.factory.createBlock([setNewValue, fireChange, subscribingWatches]) + ); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/prop.ts b/arkui-plugins/ui-plugins/property-translators/prop.ts index b8bb3d9914cf0e37a87249cf2d94c899f0caab9c..df2bce7938f7871c223864bbb2700b93a031e9aa 100644 --- a/arkui-plugins/ui-plugins/property-translators/prop.ts +++ b/arkui-plugins/ui-plugins/property-translators/prop.ts @@ -15,18 +15,21 @@ 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, - judgeIfAddWatchFunc, + collectStateManagementTypeImport, + hasDecorator, + PropertyCache, } 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'; export class PropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { @@ -34,35 +37,33 @@ export class PropTranslator extends PropertyTranslator implements InitializerCon 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') + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, @@ -97,7 +98,7 @@ 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'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), originalName ), this.property.value ?? arkts.factory.createUndefinedLiteral(), @@ -108,60 +109,92 @@ export class PropTranslator extends PropertyTranslator implements InitializerCon this.property.value ? binaryItem : arkts.factory.createTSAsExpression( - factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true), + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), this.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, false ), ]; - 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.property.typeAnnotation, + 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.property.typeAnnotation ? this.property.typeAnnotation.clone() : undefined, + 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/provide.ts b/arkui-plugins/ui-plugins/property-translators/provide.ts index ced49cefa24af2864d7a405f42e0851989cc9fa3..c1141a5ac63d1e5bf00c2f0c417c50da74482cfd 100644 --- a/arkui-plugins/ui-plugins/property-translators/provide.ts +++ b/arkui-plugins/ui-plugins/property-translators/provide.ts @@ -15,45 +15,62 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter, generateThisBackingValue, 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, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + createGetter, + generateToRecord, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + getValueInProvideAnnotation, + ProvideOptions, + hasDecorator, + PropertyCache, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; import { factory } from './factory'; 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(newName, originalName); - currentStructInfo.initializeBody.push(initializeStruct); - arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(originalName, newName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + 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 = createOptionalClassProperty( + const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, - 'MutableState', + StateManagementTypes.PROVIDE_DECORATED, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE ); - const thisValue: arkts.MemberExpression = generateThisBackingValue(newName, false, true); + 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.property.typeAnnotation, - thisValue + thisGet ); const setter: arkts.MethodDefinition = this.translateSetter( originalName, this.property.typeAnnotation, - thisValue + thisSet ); return [field, getter, setter]; @@ -62,7 +79,7 @@ export class ProvideTranslator extends PropertyTranslator implements Initializer translateGetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, - returnValue: arkts.MemberExpression + returnValue: arkts.Expression ): arkts.MethodDefinition { return createGetter(originalName, typeAnnotation, returnValue); } @@ -70,59 +87,81 @@ export class ProvideTranslator extends PropertyTranslator implements Initializer translateSetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, - left: arkts.MemberExpression + statement: arkts.AstNode ): arkts.MethodDefinition { - const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('observableProxy'), - undefined, - [arkts.factory.createIdentifier('value')] - ); - - return createSetter(originalName, typeAnnotation, left, right); + return createSetter2(originalName, typeAnnotation, statement); } - generateInitializeStruct(newName: string, originName: string): arkts.AstNode { - let provideValueStr: string | undefined = getValueInAnnotation(this.property, DecoratorNames.PROVIDE); - if (!provideValueStr) { - provideValueStr = originName; - } - const memExp: arkts.Expression = factory.createDoubleBlockStatementForOptionalExpression( - arkts.factory.createIdentifier('initializers'), - newName, - 'value' - ); - const body: arkts.BlockStatement = arkts.factory.createBlock([ - arkts.factory.createReturnStatement( - arkts.factory.createBinaryExpression( - memExp, - this.property.value, - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING - ) - ), - ]); - const script = arkts.factory.createScriptFunction( - body, - arkts.FunctionSignature.createFunctionSignature(undefined, [], this.property.typeAnnotation, false), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC - ); - const right: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier('contextLocalStateOf'), - this.property.typeAnnotation ? [this.property.typeAnnotation] : undefined, - [arkts.factory.create1StringLiteral(provideValueStr), arkts.factory.createArrowFunction(script)] - ); - return arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createThisExpression(), - arkts.factory.createIdentifier(newName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + generateInitializeStruct(originalName: string, newName: string): arkts.AstNode { + 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), + arkts.factory.createBinaryExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), + originalName ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - right + this.property.value ?? arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + ), + 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.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_PROVIDE, + this.property.typeAnnotation, + 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/regularProperty.ts b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts index 0d87d638adf8a6b237af4be829eb1bee738e6d65..7960157cd39293f2316635b8cf304232b5a095c7 100644 --- a/arkui-plugins/ui-plugins/property-translators/regularProperty.ts +++ b/arkui-plugins/ui-plugins/property-translators/regularProperty.ts @@ -15,23 +15,72 @@ import * as arkts from '@koalaui/libarkts'; -import { createGetter, createSetter } from './utils'; -import { PropertyTranslator } from './base'; +import { createGetter, generateToRecord, generateThisBacking, createSetter2, PropertyCache } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; import { backingField, expectName } from '../../common/arkts-utils'; +import { factory } from './factory'; +import { updateArrow, updateNewClassInstanceExpression } from '../customdialog'; -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 {} + isCustomDialogController(node: arkts.AstNode | undefined): boolean { + if ((node instanceof arkts.ETSNewClassInstanceExpression) && (node.getTypeRef instanceof arkts.ETSTypeReference) && + (node.getTypeRef?.part?.name instanceof arkts.Identifier) && (node.getTypeRef?.part?.name?.name === 'CustomDialogController')) { + return true; + } + return false; + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const value = this.property.value; + if (this.isCustomDialogController(value)) { + const newValue = updateNewClassInstanceExpression(value as arkts.ETSNewClassInstanceExpression, this.property.key?.name, true); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName, newValue); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } else { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName, value); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + } + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { - return [this.property]; + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, false); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + thisValue, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + 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 + ); + + return [field, getter, setter]; } translateGetter( @@ -45,23 +94,35 @@ export class regularPropertyTranslator extends PropertyTranslator implements Ini translateSetter( originalName: string, typeAnnotation: arkts.TypeNode | undefined, - left: arkts.MemberExpression + statement: arkts.AstNode ): arkts.MethodDefinition { - const right: arkts.Identifier = arkts.factory.createUndefinedLiteral(); - return createSetter(originalName, typeAnnotation, left, right); + return createSetter2(originalName, typeAnnotation, statement); } - generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { - return arkts.factory.createAssignmentExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('this'), - arkts.factory.createIdentifier(originalName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + generateInitializeStruct(newName: string, originalName: string, value: arkts.Expression): arkts.AstNode { + const binaryItem = arkts.factory.createBinaryExpression( + factory.createBlockStatementForOptionalExpression( + arkts.factory.createIdentifier('initializers'), + originalName ), + value ?? arkts.factory.createUndefinedLiteral(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING + ); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, - this.property.value ?? arkts.factory.createUndefinedLiteral() + binaryItem ); + return arkts.factory.createExpressionStatement(assign); + } +} + +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/state.ts b/arkui-plugins/ui-plugins/property-translators/state.ts index 667fabac74a82ac38516770170ca9f536560aa3c..704a56f711b3614520ceaa4cf517a9d069d44290 100644 --- a/arkui-plugins/ui-plugins/property-translators/state.ts +++ b/arkui-plugins/ui-plugins/property-translators/state.ts @@ -15,50 +15,51 @@ 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, + PropertyCache, } 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'; 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') + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, @@ -93,30 +94,67 @@ 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'), + arkts.factory.createIdentifier(CustomComponentNames.COMPONENT_INITIALIZERS_NAME), 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 - ); + 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.property.typeAnnotation, + 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/storageProp.ts b/arkui-plugins/ui-plugins/property-translators/storageProp.ts index 2693f749d866c47d3e6651bcb961979954473f58..8d3cf948a104b13fafed06bbb7f5c5fba9b92a7d 100644 --- a/arkui-plugins/ui-plugins/property-translators/storageProp.ts +++ b/arkui-plugins/ui-plugins/property-translators/storageProp.ts @@ -16,18 +16,20 @@ 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, + PropertyCache, } from './utils'; -import { createOptionalClassProperty } from '../utils'; +import { factory } from './factory'; function getStoragePropValueStr(node: arkts.AstNode): string | undefined { if (!arkts.isClassProperty(node) || !node.value) return undefined; @@ -63,72 +65,57 @@ export class StoragePropTranslator extends PropertyTranslator implements Initial 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); 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[] = [ arkts.factory.createStringLiteral(storagePropValueStr), arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), + factory.createTypeFrom(this.property.typeAnnotation) ]; - 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.property.typeAnnotation, + 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') + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, @@ -159,3 +146,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/storagelink.ts b/arkui-plugins/ui-plugins/property-translators/storagelink.ts index efd83e9d60c3adc50a715613808566a85b108ef7..8c43bd631281a5166607bf3d38c188b8cc338833 100644 --- a/arkui-plugins/ui-plugins/property-translators/storagelink.ts +++ b/arkui-plugins/ui-plugins/property-translators/storagelink.ts @@ -16,18 +16,20 @@ 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, + PropertyCache, } from './utils'; -import { createOptionalClassProperty } from '../utils'; +import { factory } from './factory'; function getStorageLinkValueStr(node: arkts.AstNode): string | undefined { if (!arkts.isClassProperty(node) || !node.value) return undefined; @@ -63,72 +65,56 @@ export class StorageLinkTranslator extends PropertyTranslator implements Initial 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); 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[] = [ arkts.factory.createStringLiteral(storageLinkValueStr), arkts.factory.create1StringLiteral(originalName), this.property.value ?? arkts.factory.createUndefinedLiteral(), + factory.createTypeFrom(this.property.typeAnnotation) ]; - 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.property.typeAnnotation, + 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') + generateGetOrSetCall(thisValue, GetSetTypes.SET) ); const getter: arkts.MethodDefinition = this.translateGetter( originalName, @@ -159,3 +145,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..24e9e3bf628b4cf0b57ac9301a5397fa1a8516b0 100644 --- a/arkui-plugins/ui-plugins/property-translators/types.ts +++ b/arkui-plugins/ui-plugins/property-translators/types.ts @@ -32,3 +32,9 @@ export interface InitializerConstructor { cacheTranslatedInitializer(newName: string, originalName: string): void; translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[]; } + +export type ClassScopeInfo = { + isObserved: boolean; + classHasTrack: boolean; + getters: arkts.MethodDefinition[] +}; diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index 95b8d8be0269fabbc4943a6a054ee1bf6ae278f3..56d189490329081257e09baec354e0f4ea0b92e2 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -14,45 +14,73 @@ */ 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', -} +import { ImportCollector } from '../../common/import-collector'; +import { isDecoratorAnnotation } from '../../common/arkts-utils'; +import { + DecoratorIntrinsicNames, + DecoratorNames, + DECORATOR_TYPE_MAP, + StateManagementTypes, + GetSetTypes, +} from '../../common/predefines'; +import { + addMemoAnnotation, + findCanAddMemoFromParameter, + findCanAddMemoFromTypeAnnotation, +} from '../../collectors/memo-collectors/utils'; -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 DecoratorInfo { + annotation: arkts.AnnotationUsage; + name: DecoratorNames; } -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)) + ); + } +} + +/** + * 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)) { return property.scriptFunction.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName)); @@ -60,43 +88,89 @@ export function hasDecorator( return property.annotations.some((anno) => isDecoratorAnnotation(anno, decoratorName)); } -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.LOCAL_STORAGE_PROP) || - hasDecorator(node, DecoratorNames.OBJECT_LINK) - ) { - return 'SyncedProperty'; +/** + * Determine whether the node `` is decorated by decorators that need initializing without assignment. + * + * @param st class property node + */ +export function needDefiniteOrOptionalModifier(st: arkts.ClassProperty): boolean { + return ( + hasDecoratorName(st, DecoratorNames.LINK) || + hasDecoratorName(st, DecoratorNames.CONSUME) || + hasDecoratorName(st, DecoratorNames.OBJECT_LINK) || + (hasDecoratorName(st, DecoratorNames.PROP) && !st.value) + ); +} + +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 'MutableState'; + 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 infos; +} + +export function getStateManagementType(decoratorInfo: DecoratorInfo): StateManagementTypes { + const decoratorName = decoratorInfo.name; + const typeName = DECORATOR_TYPE_MAP.get(decoratorName); + if (!!typeName) { + return typeName; + } + return StateManagementTypes.MUTABLE_STATE; +} + +export function collectStateManagementTypeImport(type: StateManagementTypes): void { + ImportCollector.getInstance().collectImport(type); } export function createGetter( name: string, type: arkts.TypeNode | undefined, - returns: arkts.Expression + returns: arkts.Expression, + needMemo: boolean = false ): arkts.MethodDefinition { + const returnType: arkts.TypeNode | undefined = type?.clone(); + if (needMemo && findCanAddMemoFromTypeAnnotation(returnType)) { + addMemoAnnotation(returnType); + } const body = arkts.factory.createBlock([arkts.factory.createReturnStatement(returns)]); - const scriptFunction = arkts.factory.createScriptFunction( body, - arkts.FunctionSignature.createFunctionSignature(undefined, [], type?.clone(), false), + arkts.FunctionSignature.createFunctionSignature(undefined, [], returnType, 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(name), - arkts.factory.createFunctionExpression(scriptFunction), + scriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); @@ -122,8 +196,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, @@ -135,7 +209,7 @@ export function createSetter( return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, arkts.factory.createIdentifier(name), - arkts.factory.createFunctionExpression(scriptFunction), + scriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); @@ -161,7 +235,7 @@ export function createSetter2( return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, arkts.factory.createIdentifier(name), - arkts.factory.createFunctionExpression(scriptFunction), + scriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, false ); @@ -223,47 +297,55 @@ export function getValueInAnnotation(node: arkts.ClassProperty, decoratorName: D return undefined; } -function getWatchValueStr(node: arkts.AstNode): string | undefined { - if (!arkts.isClassProperty(node) || !node.value) { - return undefined; - } - return arkts.isStringLiteral(node.value) ? node.value.str : undefined; +export interface ProvideOptions { + alias: string; + allowOverride: boolean; } -function getWatchAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { - const isWatchAnnotation: boolean = - !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.WATCH; - - if (isWatchAnnotation && anno.properties.length === 1) { - return getWatchValueStr(anno.properties.at(0)!); - } - return undefined; -} - -function getWatchValueInAnnotation(node: arkts.ClassProperty): string | undefined { +export function getValueInProvideAnnotation(node: arkts.ClassProperty): ProvideOptions | 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 = getWatchAnnotationValue(anno); - if (!!str) { - return str; + if (anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.PROVIDE) { + const alias: string = getValueInObjectAnnotation(anno, DecoratorNames.PROVIDE, 'alias'); + const allowOverride: boolean = getValueInObjectAnnotation(anno, DecoratorNames.PROVIDE, 'allowOverride') + ? true + : false; + return { alias, allowOverride }; } } + return undefined; +} +function getValueInObjectAnnotation(anno: arkts.AnnotationUsage, decoratorName: DecoratorNames, key: string): any { + const isSuitableAnnotation: boolean = + !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; + if (!isSuitableAnnotation) { + return undefined; + } + const keyItem: arkts.AstNode | undefined = anno.properties.find( + (annoProp: arkts.AstNode) => + arkts.isClassProperty(annoProp) && + annoProp.key && + arkts.isIdentifier(annoProp.key) && + annoProp.key.name === key + ); + if (keyItem && arkts.isClassProperty(keyItem) && keyItem.value) { + return getDifferentAnnoTypeValue(keyItem.value); + } return undefined; } -export function judgeIfAddWatchFunc(args: arkts.Expression[], property: arkts.ClassProperty): void { - if (hasDecorator(property, DecoratorNames.WATCH)) { - const watchStr: string | undefined = getWatchValueInAnnotation(property); - if (watchStr) { - args.push(factory.createWatchCallback(watchStr)); - } +function getDifferentAnnoTypeValue(value: arkts.Expression): string | boolean { + if (arkts.isBooleanLiteral(value)) { + return value.value; + } else if (arkts.isStringLiteral(value)) { + return value.str; } + return value.dumpSrc(); } -export function generateGetOrSetCall(beforCall: arkts.AstNode, type: string) { +export function generateGetOrSetCall(beforCall: arkts.AstNode, type: GetSetTypes) { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( beforCall, @@ -282,12 +364,12 @@ export function generateToRecord(newName: string, originalName: string): arkts.P return arkts.Property.createProperty( arkts.factory.createStringLiteral(originalName), arkts.factory.createBinaryExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('paramsCasted'), - arkts.factory.createIdentifier(originalName), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('paramsCasted'), + arkts.factory.createIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false ), arkts.ETSNewClassInstanceExpression.createETSNewClassInstanceExpression( arkts.factory.createTypeReference( @@ -300,25 +382,59 @@ 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 ''; +// CACHE +export interface PropertyCachedBody { + initializeBody?: arkts.AstNode[]; + updateBody?: arkts.AstNode[]; + toRecordBody?: arkts.Property[]; +} + +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/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 4b2d085948655c1f3101eef59aa2f8e4d49d3147..5901243d41b1c5e19f4042eeb56e44dd48cc7156 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -14,99 +14,79 @@ */ import * as arkts from '@koalaui/libarkts'; -import { CustomComponentNames, Dollars } from '../utils'; +import { + CustomComponentNames, + getCustomComponentOptionsName, + getGettersFromClassDecl, + getTypeNameFromTypeParameter, + getTypeParamsFromClassDecl, + isCustomComponentInterface, + isKnownMethodDefinition +} from '../utils'; import { factory as uiFactory } from '../ui-factory'; -import { annotation } from '../../common/arkts-utils'; +import { factory as propertyFactory } from '../property-translators/factory'; +import { collect, filterDefined } from '../../common/arkts-utils'; +import { + classifyObservedTrack, + classifyProperty, + classifyPropertyInInterface, + ClassScopeInfo, + InterfacePropertyTranslator, + PropertyTranslator, +} from '../property-translators'; +import { + CustomComponentScopeInfo, + isEtsGlobalClass, + ResourceInfo, + checkRawfileResource, + generateResourceModuleName, + generateResourceBundleName, + isDynamicName, + preCheckResourceData, + ResourceParameter, + getResourceParams, + isResourceNode, + isForEachCall, +} from './utils'; +import { collectStateManagementTypeImport, hasDecorator, PropertyCache } 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, +} from '../../common/predefines'; +import { ObservedTrackTranslator } from '../property-translators/observedTrack'; +import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; export class factory { - /* - * create `constructor() {}`. - */ - static createConstructorMethod(member: arkts.MethodDefinition): arkts.MethodDefinition { - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, - member.name, - arkts.factory.createFunctionExpression(member.scriptFunction), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, - false - ); - } - - /* - * create _build menthod. + /** + * update class `constructor` to private. */ - 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, - arkts.factory.createFunctionExpression(updateScriptFunction), - modifiers, - false - ); - } - - /* - * generate _r() or _rawfile(). - */ - 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 menthod. + /** + * 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)); modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; } const scriptFunction: arkts.ScriptFunction = arkts.factory @@ -126,28 +106,24 @@ export class factory { return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, updateKey, - arkts.factory.createFunctionExpression(scriptFunction), + scriptFunction, modifiers, false ); } - /* - * create __updateStruct menthod. + /** + * 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; } @@ -168,21 +144,21 @@ export class factory { return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, updateKey, - arkts.factory.createFunctionExpression(scriptFunction), + scriptFunction, modifiers, false ); } - /* - * create __toRecord menthod when the component is decorated with @Reusable. + /** + * 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 ) ); @@ -203,13 +179,13 @@ export class factory { return arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, arkts.factory.createIdentifier('__toRecord'), - arkts.factory.createFunctionExpression(toRecordScriptFunction), + toRecordScriptFunction, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OVERRIDE, false ); } - /* + /** * generate `const paramsCasted = (params as )`. */ static generateParamsCasted(optionsTypeName: string): arkts.VariableDeclaration { @@ -230,7 +206,7 @@ export class factory { ); } - /* + /** * generate Record type. */ static generateTypeRecord(): arkts.ETSTypeReference { @@ -245,7 +221,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static generateTypeReferenceWithTypeName(typeName: string): arkts.ETSTypeReference { @@ -254,7 +230,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static updateCustomComponentClass( @@ -274,4 +250,680 @@ export class factory { arkts.classDefinitionFlags(definition) ); } + + /** + * add headers for animation & @AnimatableExtend in CommonMethod + */ + static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + 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( + node, + node.extends, + node.id, + node.typeParams, + updatedBody, + node.isStatic, + node.isFromExternal + ); + } + + /** + * 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 { + const aniparams: arkts.Expression[] = [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) + ), + arkts.factory.createETSUndefinedType(), + ]) + ), + undefined + ), + ]; + const aniFunc = arkts.factory.createScriptFunction( + undefined, + arkts.factory.createFunctionSignature(undefined, aniparams, arkts.TSThisType.createTSThisType(), false), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(key), + aniFunc, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + /** + * transform property members in custom-component class. + */ + static tranformPropertyMembers( + propertyTranslators: PropertyTranslator[], + 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_CONSTRUCTOR_ORI) && !isDecl) { + return this.setStructConstructorToPrivate(member); + } + if (isKnownMethodDefinition(member, CustomComponentNames.COMPONENT_BUILD_ORI)) { + addMemoAnnotation(member.scriptFunction); + } + 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 propertyTranslators: PropertyTranslator[] = filterDefined( + definition.body.map((it) => classifyProperty(it, scope)) + ); + const translatedMembers: arkts.AstNode[] = this.tranformPropertyMembers( + propertyTranslators, + classOptionsName ?? getCustomComponentOptionsName(className), + scope + ); + const updateMembers: arkts.AstNode[] = definition.body + .filter((member) => !arkts.isClassProperty(member)) + .map((member: arkts.AstNode) => factory.transformNonPropertyMembersInClass(member, scope.isDecl)); + + const updateClassDef: arkts.ClassDefinition = this.updateCustomComponentClass(definition, [ + ...translatedMembers, + ...updateMembers, + ]); + return arkts.factory.updateClassDeclaration(node, updateClassDef); + } + + /** + * 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; + } + + /* + * Process string Literal type arguments for resource node. + */ + 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 + ); + } + } + + /* + * 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 (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 transformNormalClass(node: arkts.ClassDeclaration): arkts.ClassDeclaration { + if (!node.definition) { + return node; + } + if (isEtsGlobalClass(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; + }); + 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, + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); + } + const newClassDef = factory.updateObservedTrackClassDef(node.definition); + return arkts.factory.updateClassDeclaration(node, newClassDef); + } + + static updateObservedTrackClassDef(node: arkts.ClassDefinition): arkts.ClassDefinition { + const isObserved: boolean = hasDecorator(node, DecoratorNames.OBSERVED); + const classHasTrack: boolean = node.body.some( + (member) => arkts.isClassProperty(member) && hasDecorator(member, DecoratorNames.TRACK) + ); + if (!isObserved && !classHasTrack) { + return node; + } + const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( + node, + node.ident, + node.typeParams, + node.superTypeParams, + [ + ...node.implements, + arkts.TSClassImplements.createTSClassImplements( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.OBSERVED_OBJECT) + ) + ) + ), + arkts.TSClassImplements.createTSClassImplements( + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(StateManagementTypes.SUBSCRIBED_WATCHES) + ) + ) + ), + ], + undefined, + node.super, + factory.observedTrackPropertyMembers(classHasTrack, node, isObserved), + node.modifiers, + arkts.classDefinitionFlags(node) + ); + collectStateManagementTypeImport(StateManagementTypes.OBSERVED_OBJECT); + return updateClassDef; + } + + static observedTrackPropertyMembers( + classHasTrack: boolean, + definition: arkts.ClassDefinition, + isObserved: boolean + ): arkts.AstNode[] { + const watchMembers: arkts.AstNode[] = propertyFactory.createWatchMembers(); + const v1RenderIdMembers: arkts.AstNode[] = propertyFactory.createV1RenderIdMembers(); + const conditionalAddRef: arkts.MethodDefinition = propertyFactory.conditionalAddRef(); + 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, ...v1RenderIdMembers, conditionalAddRef], + ...(classHasTrack ? [] : [propertyFactory.createMetaInObservedClass()]), + ...collect(...propertyMembers), + ...nonClassPropertyOrGetter, + ...classScopeInfo.getters, + ]; + } + + /* + * 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, + arkts.factory.createBlock([createOrSetStatement, originStatements[originStatements.length - 1]]), + arkts.FunctionSignature.createFunctionSignature( + node.typeParams, + node.params, + node.returnTypeAnnotation, + node.hasReceiver + ), + node.flags, + node.modifiers + ); + } + + /* + * add arrow function type to arguments of call expression. + */ + static transformCallArguments(node: arkts.CallExpression): arkts.CallExpression { + if (!arkts.isArrowFunctionExpression(node.arguments[1])) { + return node; + } + const argTypeParam: arkts.Expression = node.arguments[1].scriptFunction.params[0]; + if ( + !arkts.isEtsParameterExpression(argTypeParam) || + !argTypeParam.type || + !arkts.isTypeNode(argTypeParam.type) + ) { + return node; + } + const referenceType = uiFactory.createComplexTypeFromStringAndTypeParameter('Array', [ + argTypeParam.type.clone(), + ]); + const newArrowArg: arkts.ArrowFunctionExpression = arkts.factory.createArrowFunction( + uiFactory.createScriptFunction({ + body: arkts.factory.createBlock([arkts.factory.createReturnStatement(node.arguments[0])]), + returnTypeAnnotation: referenceType, + flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + }) + ); + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ + newArrowArg, + ...node.arguments.slice(1), + ]); + } + + 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; + } } diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index b280b0ad5754578be18446e2fd6ee52f8fc1950b..3101184cdba3342b645ef30ed3c71c73166d4cfb 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -15,200 +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 { - CustomComponentNames, - getCustomComponentOptionsName, - getTypeNameFromTypeParameter, - getTypeParamsFromClassDecl, -} from '../utils'; -import { isCustomComponentClass, isKnownMethodDefinition, isEtsGlobalClass, isReourceNode } 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 { ScopeInfo } from './utils'; - -function tranformPropertyMembers( - className: string, - propertyTranslators: PropertyTranslator[], - optionsTypeName: string, - isDecl?: boolean, - scope?: ScopeInfo -): 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?: ScopeInfo -): 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 { PropertyCache } from '../property-translators/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; export class StructTransformer extends AbstractVisitor { - private scopeInfos: ScopeInfo[] = []; + private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; + aceBuildJson: LoaderJson; + resourceInfo: ResourceInfo; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; + this.scope = { customComponents: [] }; + this.aceBuildJson = loadBuildJson(this.projectConfig); + this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); } reset(): void { super.reset(); - this.scopeInfos = []; + this.scope = { customComponents: [] }; + PropertyCache.getInstance().reset(); + ImportCollector.getInstance().reset(); + DeclarationCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfos.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.scopeInfos.length > 0) { + if (arkts.isMethodDefinition(node) && this.scope.customComponents.length > 0) { const name = node.name.name; - const scopeInfo = this.scopeInfos.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.scopeInfos.push(scopeInfo); + this.scope.customComponents.push(scopeInfo); } } - exit(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { - this.scopeInfos.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: ScopeInfo | undefined; - if (this.scopeInfos.length > 0) { - scope = this.scopeInfos[this.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 (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 502917ecff92c43cffdb9e1b50a732650ce6918f..bb3123e317cceecd15fc5ecf8ac7e5531876e5d1 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -13,67 +13,510 @@ * limitations under the License. */ +import * as fs from 'fs'; +import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; -import { Dollars } from '../utils'; -import { CustomComponentNames } from '../utils'; +import { CustomComponentInfo } from '../utils'; +import { matchPrefix } from '../../common/arkts-utils'; +import { + ARKUI_IMPORT_PREFIX_NAMES, + Dollars, + ModuleType, + DefaultConfiguration, + LogType, + RESOURCE_TYPE, + InnerComponentNames, + ARKUI_FOREACH_SOURCE_NAME, +} from '../../common/predefines'; +import { DeclarationCollector } from '../../common/declaration-collector'; +import { ProjectConfig } from '../../common/plugin-context'; +import { LogCollector } from '../../common/log-collector'; -export type ScopeInfo = { - name: string; +export type ScopeInfoCollection = { + customComponents: CustomComponentScopeInfo[]; +}; + +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 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) { +export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { + if (node.definition?.ident?.name === 'ETSGLOBAL') { + return true; + } + return false; +} + +/** + * Determine whether it is resource node begin with `$r` or `$rawfile`. + * + * @param node call expression node + */ +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; } - const name: string = node.definition.ident.name; - const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); - return name === CustomComponentNames.COMPONENT_CLASS_NAME || structCollection.has(name); + 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; +} + +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; +} + +/** + * Read the content of file 'loader.json'. + * + * @param projectConfig configuration information of the project. + */ +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 {}; +} + +/** + * Initialize all resources information, including app resources, system resources, dependent hap resources and rawfile resources. + * + * @param projectConfig configuration information of the project. + * @param aceBuildJson content of the file 'loader.json'. + */ +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 { resourcesList, rawfile }; +} + +/** + * 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); + } + } + if (projectConfig.rawFileResource) { + processResourcesRawfile(projectConfig, projectConfig.rawFileResource, rawfile); + } +} + +/** + * 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]); + }); + } +} + +/** + * 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', + }); + } } /** - * Determine whether it is method with specified name. + * Check the format and the existance of resource string literal. * - * @param method method definition node - * @param name specified method name + * @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 isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { - if (!method || !arkts.isMethodDefinition(method)) { +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; } + 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); + } +} - // For now, we only considered matched method name. - const isNameMatched: boolean = method.name?.name === name; - return isNameMatched; +/** + * 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; } /** - * Determine whether it is ETSGLOBAL class. + * generate bundleName for $r and $rawfile. * - * @param node class declaration node + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. */ -export function isEtsGlobalClass(node: arkts.ClassDeclaration): boolean { - if (node.definition?.ident?.name === 'ETSGLOBAL') { - return true; +export function generateResourceBundleName(projectConfig: ProjectConfig, isDynamicBundleOrModule: boolean): string { + if (projectConfig.resetBundleName || projectConfig.allowEmptyBundleName) { + return ''; } - return false; + if (isDynamicBundleOrModule) { + return DefaultConfiguration.DYNAMIC_BUNDLE_NAME; + } + return projectConfig.moduleType === ModuleType.HAR + ? DefaultConfiguration.HAR_DEFAULT_BUNDLE_NAME + : projectConfig.bundleName + ? projectConfig.bundleName + : ''; } /** - * Determine whether it is resource node begin with '$r' or '$rawfile'. + * generate moduleName for $r and $rawfile. * - * @param node call expression node + * @param projectConfig project config. + * @param isDynamicBundleOrModule a flag for determining whether to use dynamic module name and bundle name. */ -export function isReourceNode(node: arkts.CallExpression): boolean { - if (node.expression.dumpSrc() === Dollars.DOLLAR_RESOURCE || node.expression.dumpSrc() === Dollars.DOLLAR_RAWFILE) { - return true; +export function generateResourceModuleName( + projectConfig: ProjectConfig, + isDynamicBundleOrModule: boolean = false, + resourceModuleName: string, + fromOtherModule: boolean +): string { + if (fromOtherModule && resourceModuleName) { + return resourceModuleName.replace(/^\[|\]$/g, ''); } - return false; + 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; } diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index 3c0c59e6b6731fd75003a9537199ba53f73d08ee..ca8dd32bf427d90631cf343cbb11931d880a8606 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -14,8 +14,43 @@ */ import * as arkts from '@koalaui/libarkts'; -import { BuilderLambdaNames, CustomComponentNames } from './utils'; -import { annotation } from '../common/arkts-utils'; +import { + BuilderLambdaNames, + CustomComponentAnontations, + CustomComponentNames, + hasNullOrUndefinedType, + hasPropertyInAnnotation, +} from './utils'; +import { PartialExcept, PartialNested, PartialNestedExcept } from '../common/safe-types'; +import { 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 { /** @@ -63,16 +98,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 */ @@ -111,8 +136,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; } @@ -125,6 +150,18 @@ export class factory { ); } + /** + * create complex type from string and type parameter, e.g. `Set` + */ + static createComplexTypeFromStringAndTypeParameter(name: string, params: 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`. */ @@ -144,25 +181,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 - ); - arkts.importDeclarationInsert(importDecl, program); - return; - } - - /* * create `import { as } ...`. */ static createAdditionalImportSpecifier(imported: string, local: string): arkts.ImportSpecifier { @@ -172,58 +190,211 @@ 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, - arkts.factory.createFunctionExpression(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 `@memo() _build(<>)`. + /** + * 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; + } + + /** + * 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, - arkts.factory.createFunctionExpression(updateScriptFunction), - modifiers, + ]), + ]; + 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 { + let annotations: readonly arkts.AnnotationUsage[] = property.annotations; + if (annotations.length === 0) { + return; + } + const newAnnos: arkts.AnnotationUsage[] = annotations.map((anno: arkts.AnnotationUsage) => { + if ( + !!anno.expr && + arkts.isIdentifier(anno.expr) && + anno.expr.name === DecoratorNames.PROVIDE && + !hasPropertyInAnnotation(anno, 'alias') && + property.key && + arkts.isIdentifier(property.key) + ) { + return arkts.factory.update1AnnotationUsage(anno, anno.expr, [ + ...anno.properties, + factory.createAliasClassProperty(property.key), + ]); + } else { + return anno; + } + }); + property.setAnnotations(newAnnos); + } + + /** + * create class property : `alias: `. + */ + static createAliasClassProperty(value: arkts.Identifier): arkts.ClassProperty { + return arkts.factory.createClassProperty( + arkts.factory.createIdentifier('alias'), + arkts.factory.create1StringLiteral(value.name), + undefined, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, false ); } + + /** + * add optional or definite modifier for class property needs initializing without assignment. + */ + 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; + } else { + 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; + } } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index c73d54b14e8e35c0e48245dd84fc280a51e7b95d..562c74f4ed6619f53fcd07cf76251a9d9a615122 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -14,23 +14,27 @@ */ import * as arkts from '@koalaui/libarkts'; +import { matchPrefix } from '../common/arkts-utils'; +import { ARKUI_IMPORT_PREFIX_NAMES, StructDecoratorNames } from '../common/predefines'; +import { DeclarationCollector } from '../common/declaration-collector'; 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 = '@ohos.arkui.component', COMPONENT_CLASS_NAME = 'CustomComponent', + COMPONENT_V2_CLASS_NAME = 'CustomComponentV2', 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', + CUSTOMDIALOG_ANNOTATION_NAME = 'CustomDialog', + CUSTOMDIALOG_CONTROLLER = 'CustomDialogController', + CUSTOMDIALOG_CONTROLLER_OPTIONS = 'CustomDialogControllerOptions', + SETDIALOGCONTROLLER_METHOD = '__setDialogController__', } export enum BuilderLambdaNames { @@ -39,13 +43,24 @@ export enum BuilderLambdaNames { TRANSFORM_METHOD_NAME = '_instantiateImpl', STYLE_PARAM_NAME = 'style', STYLE_ARROW_PARAM_NAME = 'instance', - CONTENT_PARAM_NAME = 'content', + CONTENT_PARAM_NAME = 'content' } -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( @@ -62,22 +77,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 ?? []; } @@ -86,34 +109,182 @@ export function getTypeNameFromTypeParameter(node: arkts.TSTypeParameter | undef return node?.name?.name; } -export function createOptionalClassProperty( - name: string, - property: arkts.ClassProperty, - stageManagementIdent: string, - modifiers: arkts.Es2pandaModifierFlags -): arkts.ClassProperty { - const newProperty = arkts.factory.createClassProperty( - arkts.factory.createIdentifier(name), - undefined, - stageManagementIdent.length - ? createStageManagementType(stageManagementIdent, property) - : property.typeAnnotation?.clone(), - modifiers, - false +// 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) + ) as arkts.MethodDefinition[]; +} + +// 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 ); - 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(), - ]) - ) +} + +// 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 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 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 + ) { + 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?.reusable && { customdialog: anno }), + }; + } + if (!isCustomComponent) { + return undefined; + } + } + 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 + ); + } + return name === scopeInfo.name; +} + +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) ); + return checkPrefix && checkComponent; +} + +export function getCustomComponentOptionsName(className: string): string { + return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; +} + +/** + * Determine whether it is method with specified name. + * + * @param method method definition node + * @param name specified method name + */ +export function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { + if (!method || !arkts.isMethodDefinition(method)) { + return false; + } + + // For now, we only considered matched method name. + const isNameMatched: boolean = method.name?.name === name; + return isNameMatched; } diff --git a/arkui-plugins/ui-syntax-plugins/index.ts b/arkui-plugins/ui-syntax-plugins/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..250f6db37bbb7aba5a04a047c90f2b5ed78b2843 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/index.ts @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 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); + 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)) { + 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; + }); +} + +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)) { + 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; +} diff --git a/arkui-plugins/ui-syntax-plugins/processor/index.ts b/arkui-plugins/ui-syntax-plugins/processor/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..44a83e261a22564d9a3027eaa153d74d2495cc03 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/processor/index.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 arkts from '@koalaui/libarkts'; +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 = { + setProjectConfig(projectConfig: ProjectConfig): void; + beforeTransform(): void; + afterTransform(): void; + parsed(node: arkts.AstNode): void; + checked(node: arkts.AstNode): void; +}; + +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 + ); + if (options.fix) { + const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(diagnosticKind); + const fixSuggestion = options.fix(options.node); + const suggestionKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_SUGGESTION + ); + const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create( + suggestionKind, + fixSuggestion.code + ); + const [startPosition, endPosition] = fixSuggestion.range; + const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); + arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo, sourceRange); + } else { + arkts.Diagnostic.logDiagnostic(diagnosticKind, arkts.getStartPosition(options.node)); + } + + // todo + const position = arkts.getStartPosition(options.node); + if (options.fix) { + const suggestion = options.fix(options.node); + console.log(`syntax-${options.level ?? 'error'}: ${message} (${position.index()},${position.line()})`); + 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-${options.level ?? 'error'}: ${message} (${position.index()},${position.line()})`); + } + } + + 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]) => { + 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..03c1707e5b55b7ae9ee503600a9fe038e81c9b75 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/attribute-no-invoke.ts @@ -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 * as arkts from '@koalaui/libarkts'; +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.chainJudgment(node)) { + this.attributeNoInvoke(node); + } + } + + private attributeNoInvoke(node: arkts.AstNode): void { + const childNode = node.getChildren(); + if (!Array.isArray(childNode) || childNode.length < 1) { + return; + } + if (arkts.isMemberExpression(childNode[0]) && arkts.isIdentifier(childNode[0].property)) { + this.report({ + node, + message: this.messages.cannotInitializePrivateVariables, + data: { + componentNode: node.dumpSrc(), + }, + }); + } + } + + private chainJudgment(node: arkts.AstNode): boolean { + let children = node.getChildren(); + while (true) { + if (!children || children.length === 0) { + return false; + } + const firstChild = children[0]; + if (arkts.isIdentifier(firstChild)) { + break; + } + if (!arkts.isMemberExpression(firstChild) && !arkts.isCallExpression(firstChild)) { + return false; + } + children = firstChild.getChildren(); + } + return true; + } +}; + +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 new file mode 100644 index 0000000000000000000000000000000000000000..a914891f9a65984a9b399a34ac34dbb40f4000cf --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getAnnotationUsage, PresetDecorators, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const STATEMENT_LENGTH: number = 1; +const BUILD_COUNT_LIMIT: number = 1; + +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.`, + }; + } + + 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); + }); + } + + 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; + } + + 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, + }); + } + } + + private isContainerComponent(componentName: string): boolean { + const loadedContainerComponents = this.context.componentsInfo.containerComponents; + if (!componentName || !loadedContainerComponents) { + return false; + } + return loadedContainerComponents.includes(componentName); + } + + 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(); + } + } +} + + +export default BuildRootNodeRule; 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..ac532a3e906f50277ca0328d564a391b1fbc946a --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/builderparam-decorator-check.ts @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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[] = []; + + 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); + } + }); + } + + 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; + } + if (!arkts.isIdentifier(node) || !this.structNameWithMultiplyBuilderParam.includes(getIdentifierName(node))) { + return; + } + if (!this.hasBlockStatement(node)) { + return; + } + let structName: string = getIdentifierName(node); + 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 new file mode 100644 index 0000000000000000000000000000000000000000..21cab0ab1a3c1015fd439f407dc13579f51e2121 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.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 { getClassPropertyName, isPrivateClassProperty } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +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(); + } + + 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, + }, + }); + } + }); + }); + } + + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..e3d8f3446e02765464ef4d452751bcf75a36ce80 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.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 { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { + forbiddenUseStateType, + getAnnotationUsage, getClassPropertyAnnotationNames, getClassPropertyName, + getClassPropertyType, PresetDecorators +} from '../utils'; + +const forbiddenUseStateTypeForDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.OBJECT_LINK, + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STORAGE_PROP, + PresetDecorators.STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + 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; + } + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + if (!componentDecorator) { + return; + } + node.definition.body.forEach(member => { + this.checkDecoratedPropertyType(member, forbiddenUseStateTypeForDecorators, forbiddenUseStateType); + }); + } + + 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: this.messages.invalidDecoratedPropertyType, + data: { decoratorName, propertyName, propertyType }, + }); + } + } +} + +export default CheckDecoratedPropertyTypeRule; \ 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..42c497eb713c8cc2b1a0cb47ef43e23a3423cbc9 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts @@ -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. + */ + +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) { + if (!node.parent) { + return; + } + const parentNode = node.parent; + this.report({ + node: parentNode, + message: this.messages.componentInitLinkCheck, + fix: () => { + return { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..6a1a271f815a3b30915018a8ff375d0f2e11cd00 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-mix-use-check.ts @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const v1ComponentDecorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, +]; + +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; + } + }); + } + + 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); + } + } + } + + for (const child of node.getChildren()) { + this.findAllTSTypeAliasDeclaration(child); + } + } + + 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); + } + } + + private processComponentAnnotations( + node: arkts.StructDeclaration + ): void { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + const annotationName = getIdentifierName(anno.expr); + if ( + annotationName === PresetDecorators.COMPONENT_V2 || + annotationName === PresetDecorators.COMPONENT_V1 + ) { + this.traverseTree(node, annotationName); + } + }); + } + + 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); + } + } + + 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 = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + } + + private checkObservedConflict( + node: arkts.ClassProperty, + componentDecorators: string[] + ): void { + node.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + + const annotationName = getIdentifierName(anno.expr); + if (annotationName && componentDecorators.includes(annotationName)) { + this.report({ + node: anno, + message: this.messages.observedv2_v1, + data: { + annotation: annotationName, + }, + }); + } + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..7db39458228955d4c9ae1a445ab357ba01ce48a7 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-mix-check.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 { getAnnotationUsage, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ComponentV2MixCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + conflictWithComponentV2: `The struct '{{structName}}' can not be decorated with '@ComponentV2' and '@Component', '@Reusable', '@CustomDialog' at the same time.`, + }; + } + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..17c4bc4fc57617136ef3b1fe43f4aba3ba86a4fa --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getAnnotationUsage, getClassPropertyName, getIdentifierName +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; + +class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { + private componentV2PropertyMap: Map> = new Map(); + + public beforeTransform(): void { + this.componentV2PropertyMap = new Map(); + } + + public setup(): Record { + return { + multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in decorators.`, + 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.`, + }; + } + + public parsed(node: arkts.AstNode): void { + this.initComponentV2PropertyMap(node, this.componentV2PropertyMap); + this.checkInitializeRule(node, this.componentV2PropertyMap); + if (arkts.isMethodDefinition(node)) { + // Rule 5: Local, Param, Event decorators must be used with Property + this.checkuseStateDecoratorsWithProperty(node); + } + if (!arkts.isStructDeclaration(node)) { + return; + } + this.validateClassPropertyDecorators(node); + } + + private hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, + PresetDecorators.COMPONENT_V2); + + private hasComponent = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, + PresetDecorators.COMPONENT_V1); + + 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, + }); + } + } + + 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 { + range: [startPosition, startPosition], + code: `@${PresetDecorators.REQUIRE} `, + }; + }, + }); + } + }; + + 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) => { + const startPosition = requireDecorator.startPosition; + const endPosition = requireDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + }; + + private checkuseStateDecoratorsWithProperty(method: arkts.MethodDefinition): void { + method.scriptFunction.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && builtInDecorators.includes(annotation.expr.name)) { + const annotationName = annotation.expr.name; + this.reportInvalidDecoratorOnMethod(annotation, annotationName); + } + }); + } + + private reportInvalidDecoratorOnMethod(annotation: arkts.AnnotationUsage, + annotationName: string): void { + this.report({ + node: annotation, + message: this.messages.useStateDecoratorsWithProperty, + data: { annotationName }, + fix: (annotation) => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateClassPropertyDecorators(node: arkts.StructDeclaration): void { + const isComponentV2 = this.hasisComponentV2(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); + + // Rule 2: @Param without default value must be combined with @Require + this.checkParamRequiresRequire(member, propertyDecorators); + + // 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()); + } + 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 { + 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 { + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + } +}; + +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..e47c33e7ffbe2c580f22ce86d51cc56fa94d5731 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getAnnotationName, getAnnotationUsage, BUILD_NAME } 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' decorator 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 = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === 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 = member.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (computedDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + const startPosition = computedDecorator.startPosition; + const endPosition = computedDecorator.endPosition; + return { + 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) => { + const startPosition = computedDecorator.startPosition; + const endPosition = computedDecorator.endPosition; + return { + 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, + fix: (currentNode) => { + const startPosition = currentNode.startPosition; + const endPosition = currentNode.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + + 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 { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private validateClassBody(node: arkts.ClassDeclaration): void { + const observedV2Decorator = node.definition?.annotations.find(annotation => + getAnnotationName(annotation) === PresetDecorators.OBSERVED_V2 + ); + + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + + this.validateComputedInClass(node, member, observedV2Decorator); + const computedDecorator = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === 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 = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (computedDecorator && !componentV2Decorator && !componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = node.startPosition; + const endPosition = node.startPosition; + return { + 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 { + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}`, + }; + }, + }); + } + } + + private validateComputedInClass( + node: arkts.AstNode, + member: arkts.MethodDefinition, + observedV2Decorator: arkts.AnnotationUsage | undefined, + ): void { + const computedDecorator = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (computedDecorator && !observedV2Decorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = node.startPosition; + return { + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } + } +} + +export default ComputedDecoratorCheckRule; + diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts new file mode 100644 index 0000000000000000000000000000000000000000..c57f218a174ea24c7dcf90c31dbd3b531d7e052d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getIdentifierName, PresetDecorators } from '../utils'; + + +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}}'.`, + }; + } + + public beforeTransform(): void { + this.linkMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + this.initMap(node); + this.checkInitializeWithLiteral(node); + } + + private recordStructWithLinkDecorators(item: arkts.AstNode, structName: string): void { + if (!arkts.isClassProperty(item)) { + return; + } + const ClassPropertyAnnotations = getClassPropertyAnnotationNames(item); + + if (!ClassPropertyAnnotations.includes(PresetDecorators.OBJECT_LINK) && + !ClassPropertyAnnotations.includes(PresetDecorators.LINK)) { + return; + } + const annotationName = getClassPropertyName(item); + if (!annotationName) { + return; + } + this.linkMap.set(structName, annotationName); + } + + 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); + }); + }); + } + + 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; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !property.value) { + return; + } + const key: string = getIdentifierName(property.key); + if (key === '') { + 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'); + const parameter: string = this.linkMap.get(componentName)!; + this.report({ + node: property, + message: this.messages.initializerIsLiteral, + data: { + initializerName: initializerName, + parameter: `@${parameter}`, + parameterName: key, + }, + }); + }); + }); + } +} + +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..05e71cf2a59e22892af3c0892fc632374f6c01e9 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter.ts @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 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, [PresetDecorators.REGULAR]], + [PresetDecorators.VARIABLE, [PresetDecorators.LINK]], + [PresetDecorators.LOCAL_STORAGE_LINK, [PresetDecorators.REGULAR]], + [PresetDecorators.LOCAL_STORAGE_PROP, [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, + PresetDecorators.LOCAL_STORAGE_LINK, PresetDecorators.LOCAL_STORAGE_PROP, 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.context.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.context.report({ + node: property, + message: this.messages.initializerIsBuilder, + data: { + initializerName: property.value.name, + parameterName: childName, + }, + }); + } + } + if (childType === PresetDecorators.BUILDER_PARAM && !isBuilder && !isBuilderInStruct) { + this.context.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.context.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 new file mode 100644 index 0000000000000000000000000000000000000000..a16e30adb9a2cd798b7e153ed9797277abac0592 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 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 { + providerAndConsumerOnlyOnProperty: `'@{{decorator}}' can only decorate member property.`, + multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in decorators.`, + providerAndConsumerOnlyInStruct: `The '@{{decorator}}' decorator can only be used with 'struct'.`, + forbiddenInitialization: `The '@{{decorator}}' property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, + }; + } + + public beforeTransform(): void { + this.componentV2WithConsumer = new MultiMap(); + this.componentV2WithProvider = new MultiMap(); + } + + public parsed(node: arkts.AstNode): void { + this.collectStructsWithConsumerAndProvider(node); + this.validateDecoratorsOnMember(node); + this.validateOnlyInStruct(node); + + if (arkts.isCallExpression(node)) { + 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: () => { + const startPosition = otherDecorators.startPosition; + const endPosition = otherDecorators.endPosition; + return { + 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) => { + const startPosition = decorator.startPosition; + const endPosition = decorator.endPosition; + return { + 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 { + 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 { + range: [startPosition, endPosition], + code: '', + }; + } + }); + } + } +} + +export default ConsumerProviderDecoratorCheckRule; + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d35cf99f296b9971af47bdca044a45079d0b72c7 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/custom-dialog-missing-controller.ts @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { getClassPropertyType, PresetDecorators, getAnnotationUsage, isClassPropertyOptional } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const CUSTOM_DIALOG_CONTROLLER: string = 'CustomDialogController'; + +class CustomDialogMissingControllerRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingController: `The @CustomDialog decorated custom component must contain a property of the CustomDialogController type.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + this.checkMissingController(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; + } + + // 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/entry-localstorage-check.ts b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts new file mode 100644 index 0000000000000000000000000000000000000000..118755b8c0907443feb34d8eade4e7a6dae0bfb9 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-localstorage-check.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class EntryLocalStorageCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + entryLocalStorageCheck: `'@Entry' should have a parameter, like '@Entry ({ storage: "__get_local_storage__" })'.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + 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 && node.definition.annotations[0].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.LOCAL_STORAGE_PROP); + }); + + // If @LocalStorageLink is used but @Entry(storage) is missing, report error + if (entryDecorator && localStorageLinkUsed && !isStorageUsed) { + this.report({ + node: entryDecorator, + message: this.messages.entryLocalStorageCheck + }); + } + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..2802a8c0be4616a24834dce6cc2fbd24fab436f8 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/entry-struct-no-export.ts @@ -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 * as arkts from '@koalaui/libarkts'; +import { getAnnotationUsage, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class EntryStructNoExportRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noExportWithEntry: `It's not a recommended way to export struct with '@Entry' decorator, 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; + } + // Get the usage of the @Entry decorator + const entryDecoratorUsage = getAnnotationUsage( + node, + PresetDecorators.ENTRY, + ); + + //Determines whether the struct is exported + const isStructExport = node.isExport; + const isStructDefaultExport = node.isDefaultExport; + + // If a @Entry decorator is present and the struct is exported + if (entryDecoratorUsage && (isStructExport || isStructDefaultExport)) { + this.report({ + node: entryDecoratorUsage, + message: this.messages.noExportWithEntry, + }); + } + } +}; + +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 new file mode 100644 index 0000000000000000000000000000000000000000..bc35c6ef33db0f0e9a92969048a6141a581a7f8b --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/index.ts @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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 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 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 NoDuplicateDecoratorsRule from './no-duplicate-decorators'; +import NoDuplicateEntryRule from './no-duplicate-entry'; +import NoDuplicateIdRule from './no-duplicate-id'; +import NoDuplicatePreviewRule from './no-duplicate-preview'; +import NoDuplicateStateManagerRule from './no-duplicate-state-manager'; +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 StructMissingDecoratorRule from './struct-missing-decorator'; +import StructPropertyDecoratorRule from './struct-property-decorator'; +import TrackDecoratorCheckRule from './track-decorator-check'; +import TypeDecoratorCheckRule from './type-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 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: Array = [ + [AttributeNoInvokeRule, 'warn'], + [BuildRootNodeRule, 'error'], + [BuilderParamDecoratorCheckRule, 'error'], + [CheckConstructPrivateParameterRule, 'warn'], + [CheckDecoratedPropertyTypeRule, 'error'], + [ComponentComponentV2MixUseCheckRule, 'error'], + [ComponentV2MixCheckRule, 'error'], + [ConstructParameterLiteralRule, 'warn'], + [ConstructParameterRule, 'error'], + [ConsumerProviderDecoratorCheckRule, 'error'], + [ComponentV2StateUsageValidationRule, 'error'], + [CustomDialogMissingControllerRule, 'error'], + [EntryLocalStorageCheckRule, 'warn'], + [EntryStructNoExportRule, 'warn'], + [MonitorDecoratorCheckRule, 'error'], + [NestedRelationshipRule, 'error'], + [NestedReuseComponentCheckRule, 'error'], + [NoChildInButtonRule, 'error'], + [NoDuplicateDecoratorsRule, 'warn'], + [NoDuplicateEntryRule, 'error'], + [NoDuplicateIdRule, 'warn'], + [NoDuplicatePreviewRule, 'error'], + [NoDuplicateStateManagerRule, 'error'], + [NoPropLinkObjectLinkInEntryRule, 'warn'], + [NoSameAsBuiltInAttributeRule, 'error'], + [ReuseAttributeCheckRule, 'error'], + [StructMissingDecoratorRule, 'error'], + [StructPropertyDecoratorRule, 'error'], + [TrackDecoratorCheckRule, 'error'], + [TypeDecoratorCheckRule, 'error'], + [ValidateBuildInStructRule, 'error'], + [WrapBuilderCheckRule, 'error'], + [StructPropertyOptionalRule, 'warn'], + [StructVariableInitializationRule, 'error'], + [ObservedHeritageCompatibleCheckRule, 'error'], + [ObservedObservedV2Rule, 'error'], + [ObservedV2TraceUsageValidationRule, 'error'], + [OnceDecoratorCheckRule, 'error'], + [OneDecoratorOnFunctionMethodRule, 'error'], + [ComputedDecoratorCheckRule, 'error'], + [ComponentComponentV2InitCheckRule, 'error'], + [ReusableV2DecoratorCheckRule, 'error'], + [VariableInitializationViaComponentConstructorRule, 'error'], + [StructNoExtendsRule, 'error'], + [UiConsistentCheckRule, 'warn'], + [ValidateDecoratorTargetRule, 'error'], + [WatchDecoratorFunctionRule, 'error'], + [WatchDecoratorRegularRule, 'error'], + [OldNewDecoratorMixUseCheckRule, 'error'], + [RequireDecoratorRegularRule, 'error'], + [ReusableComponentInV2CheckRule, 'warn'], + [SpecificComponentChildrenRule, 'error'], +]; + +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..198852e073a158a12f78b8fd54249d76e8b185e2 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/main-pages-entry-check.ts @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, 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' decorator. ` + }; + } + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isEtsScript(node)) { + return; + } + const currentFilePath = this.getPath(); + 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; + } + const entryDocoratorUsage = getAnnotationUsage( + child, + PresetDecorators.ENTRY, + ); + if (entryDocoratorUsage) { + ++entryDecoratorCount; + } + } + } + if (entryDecoratorCount === 0) { + this.report({ + node: firstStructDeclaration ? firstStructDeclaration : node, + message: this.messages.mainPagesEntryCheck, + }); + } + } + + private getPath(): string | undefined { + const contextPtr = arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + return program.globalAbsName; + } + return undefined; + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..a096d26da0189e931edb5e16e420a2d584806c7d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +const MONITOR_COUNT_LIMIT = 1; + +class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + monitorUsedAlone: + `The member property or method can not be decorated by multiple built-in decorators.`, + monitorUsedInObservedV2Class: + `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, + monitorUsedInComponentV2Struct: + `The '@Monitor' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, + monitorDecorateMethod: + `@Monitor can only decorate method.`, + duplicatedMonitor: `Duplicate decorators for method are not allowed.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { + return; + } + + let monitorUsed = false; + + const isObservedV2 = arkts.isClassDeclaration(node) && this.checkIfClassIsObservedV2(node); + const isComponentV2 = arkts.isStructDeclaration(node) && this.checkIfStructIsComponentV2(node); + + monitorUsed = this.checkMultipleDecorators(node); + + // Check for errors related to @Monitor usage + if (monitorUsed && !isObservedV2 && arkts.isClassDeclaration(node)) { + this.checkInvalidUsage( + node, + this.messages.monitorUsedInObservedV2Class, + `@${PresetDecorators.OBSERVED_V2}` + ); + } + if (monitorUsed && !isComponentV2 && arkts.isStructDeclaration(node)) { + this.checkInvalidUsage( + node, + this.messages.monitorUsedInComponentV2Struct, + `@${PresetDecorators.COMPONENT_V2}\n` + ); + } + this.checkDecorateMethod(node); + } + private checkIfClassIsObservedV2(node: arkts.ClassDeclaration): boolean { + return node.definition?.annotations?.some( + observedV2 => observedV2.expr && arkts.isIdentifier(observedV2.expr) && + observedV2.expr?.name === PresetDecorators.OBSERVED_V2 + ) ?? false; + + } + + private checkIfStructIsComponentV2(node: arkts.StructDeclaration): boolean { + return node.definition.annotations?.some( + componentV2 => componentV2.expr && arkts.isIdentifier(componentV2.expr) && + componentV2.expr?.name === PresetDecorators.COMPONENT_V2 + ) ?? false; + } + + private checkMultipleDecorators( + node: arkts.ClassDeclaration | arkts.StructDeclaration + ): boolean { + // Traverse body of the class to check for @Monitor usage + let monitorUsed: boolean = false; + + node.definition?.body.forEach((body) => { + if (!arkts.isMethodDefinition(body)) { + return; + } + + const duplicatedMonitor = this.getMonitor(body); + const localMonitorUsed = this.getLocalMonitorUsed(body); + + if (duplicatedMonitor.length > MONITOR_COUNT_LIMIT && localMonitorUsed) { + this.reportDuplicatedMonitor(localMonitorUsed); + } + + if (localMonitorUsed) { + monitorUsed = true; + this.checkConflictingDecorators(body, localMonitorUsed); + return; // Stop further checks for this method + } + } + ); + + return monitorUsed; + } + + private getMonitor(body: arkts.MethodDefinition): arkts.AnnotationUsage[] { + const monitor = body.scriptFunction.annotations?.filter( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + return monitor; + } + + 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; + } + return false; + } + + 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); + const startPosition = startPositions[0]; + const endPosition = endPositions[endPositions.length - 1]; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportDuplicatedMonitor(localMonitorUsed: arkts.AstNode): void { + this.report({ + node: localMonitorUsed, + message: this.messages.duplicatedMonitor, + fix: () => { + const startPosition = localMonitorUsed.startPosition; + const endPosition = localMonitorUsed.endPosition; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private checkInvalidUsage(node: arkts.ClassDeclaration | arkts.StructDeclaration, message: string, fixCode: string): void { + let monitor: arkts.AnnotationUsage | undefined; + node.definition?.body.forEach((body) => { + if (!arkts.isMethodDefinition(body)) { + return; + } + monitor = this.getLocalMonitorUsed(body); + if (!monitor) { + return; + } + this.report({ + node: monitor, + message, + fix: () => ({ + range: [node.startPosition, node.startPosition], + code: fixCode + }) + }); + }); + } + + 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; + } + + const monitorDecorator = body.annotations?.find( + (annotation) => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + + if (monitorDecorator === undefined) { + return; + } + this.report({ + node: monitorDecorator, + message: this.messages.monitorDecorateMethod, + fix: () => { + const startPosition = monitorDecorator.startPosition; + const endPosition = monitorDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..fc07a421c8d6e8cd4fda1cca6b5516481bcbfeac --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-relationship.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 arkts from '@koalaui/libarkts'; +import { + getIdentifierName, + isAtomicComponent, + isBuildInComponent, + isSingleChildComponent, + listToString, + SINGLE_CHILD_COMPONENT +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +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.`, + delegateChildrenComponentParent: `The component '{{parentComponentName}}' can only have the child component '{{childComponentList}}'.`, + delegateParentComponent: `The '{{componentName}}' component can only be nested in the '{{parentComponentList}}' parent component.`, + }; + } + public parsed(node: arkts.StructDeclaration): void { + this.checkValidParentComponent(node); + this.checkValidChildComponent(node); + this.checkSingleChildComponent(node); + this.checkNoChildComponent(node); + } + + 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)) { + 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)!; + const parentComponentList: string = listToString(parentComponentListArray); + this.report({ + node: node, + message: this.messages.delegateParentComponent, + data: { + componentName: componentName, + parentComponentList: parentComponentList, + }, + }); + } + } + + private checkValidChildComponent(node: arkts.AstNode): void { + // Check whether the node is an identifier and whether there are restrictions on subcomponents + if (!arkts.isIdentifier(node)) { + return; + } + const parentComponentName: string = getIdentifierName(node); + if (!this.context.componentsInfo.validChildComponent.has(parentComponentName) || !node.parent) { + return; + } + let parentNode = node.parent; + if (!arkts.isCallExpression(parentNode) || !arkts.isIdentifier(parentNode.expression)) { + return; + } + // 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) { + return; + } + if (!arkts.isCallExpression(statement.expression) || !statement.expression.expression || + !arkts.isIdentifier(statement.expression.expression)) { + return; + } + const componentName = getIdentifierName(statement.expression.expression); + const childComponentListArray: string[] = this.context.componentsInfo.validChildComponent.get(parentComponentName)!; + if (!childComponentListArray.includes(componentName) && isBuildInComponent(this.context, componentName)) { + const childComponentList: string = listToString(childComponentListArray); + this.report({ + node: parentNode, + message: this.messages.delegateChildrenComponentParent, + data: { + parentComponentName: parentComponentName, + childComponentList: childComponentList, + }, + }); + } + }); + }); + } + 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 } + }); + } + }); + } + + 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 } + }); + } + }); + } +} + +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..16fbec2976fecaee037acd7b252c817656359c3d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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)) { + return; + } + // 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 { + 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 { + 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 { + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private reportNoReusableV2InComponentV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InComponentV1, + fix: (node) => { + return { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..8673700ac2b586656c340f7038db807562f428c6 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts @@ -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 * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { getIdentifierName } from '../utils'; + + +class NoChildInButtonRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + noChildInButton: `The Button component with a label parameter can not have any child.`, + }; + } + + public parsed(node: arkts.AstNode): void { + // Check if the current node is an identifier + if (!arkts.isIdentifier(node)) { + return; + } + const componentName = getIdentifierName(node); + // If the current node is 'Button' + if (componentName !== 'Button') { + return; + } + if (!this.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; + }; + // Gets and traverses all the children of the parent node + 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 { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } + +}; + +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 new file mode 100644 index 0000000000000000000000000000000000000000..8277b9facf85aa813e7d9246e8272d8eeff7e280 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-decorators.ts @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; + +//Unsupported decorators +const unsupportedDecorators = [ + PresetDecorators.ENTRY, + PresetDecorators.PREVIEW, +]; + +class NoDuplicateDecoratorsRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + duplicateFunctionDecorators: `Duplicate '{{decoratorName}}' decorators for function are not allowed.`, + duplicateMethodDecorators: `Duplicate '{{decoratorName}}' decorators for method are not allowed.`, + duplicateStructDecorators: `Duplicate '{{decoratorName}}' decorators for struct are not allowed.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + this.checkForDuplicateStructDecorators(node); + } else if (arkts.isFunctionDeclaration(node)) { + this.checkForDuplicateFunctionDecorators(node); + } else if (arkts.isMethodDefinition(node)) { + this.checkForDuplicateMethodDecorators(node); + } + } + + private checkForDuplicateStructDecorators( + 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 (unsupportedDecorators.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]; + this.reportStructDuplicateDecorator(prevAnnotation, decoratorName); + } + // For the last occurrence, report an error but do not provide a fix + const lastAnnotation = annotations[annotations.length - 1]; + this.report({ + node: lastAnnotation, + message: this.messages.duplicateStructDecorators, + data: { decoratorName }, + }); + }); + } + + private reportStructDuplicateDecorator( + annotation: arkts.AnnotationUsage, + decoratorName: string): void { + this.report({ + node: annotation, + message: this.messages.duplicateStructDecorators, + data: { decoratorName }, + fix: () => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private checkForDuplicateFunctionDecorators( + functionNode: arkts.FunctionDeclaration + ): void { + const annotations = functionNode.annotations; + if (!annotations || annotations.length <= 1) { + return; + } + const decoratorCounts: Map = new Map(); + for (const annotation of annotations) { + const decoratorName = getAnnotationName(annotation); + if (unsupportedDecorators.includes(decoratorName)) { + continue; + } + if (decoratorCounts.has(decoratorName)) { + const info = decoratorCounts.get(decoratorName)!; + info.count += 1; + info.annotations.push(annotation); + } else { + decoratorCounts.set(decoratorName, { count: 1, annotations: [annotation] }); + } + } + decoratorCounts.forEach(({ count, annotations }, decoratorName) => { + if (count <= 1) { + return; + } + + // Keep the last one and delete the rest + for (let i = 0; i < annotations.length - 1; i++) { + this.reportFunctionDuplicateDecorator(annotations[i], decoratorName); + } + + // The last one doesn't provide a fix + const last = annotations[annotations.length - 1]; + this.report({ + node: last, + message: this.messages.duplicateFunctionDecorators, + data: { decoratorName }, + }); + }); + } + + private reportFunctionDuplicateDecorator( + annotation: arkts.AnnotationUsage, + decoratorName: string): void { + this.report({ + node: annotation, + message: this.messages.duplicateFunctionDecorators, + data: { decoratorName }, + fix: () => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private checkForDuplicateMethodDecorators( + methodNode: arkts.MethodDefinition + ): void { + const annotations = methodNode.scriptFunction.annotations; + if (!annotations || annotations.length <= 1) { + return; + } + const decoratorCounts: Map = new Map(); + for (const annotation of annotations) { + const decoratorName = getAnnotationName(annotation); + if (unsupportedDecorators.includes(decoratorName)) { + continue; + } + if (decoratorCounts.has(decoratorName)) { + const info = decoratorCounts.get(decoratorName)!; + info.count += 1; + info.annotations.push(annotation); + } else { + decoratorCounts.set(decoratorName, { count: 1, annotations: [annotation] }); + } + } + decoratorCounts.forEach(({ count, annotations }, decoratorName) => { + if (count <= 1) { + return; + } + for (let i = 0; i < annotations.length - 1; i++) { + this.reportMethodDuplicateDecorator(annotations[i], decoratorName); + } + const last = annotations[annotations.length - 1]; + this.report({ + node: last, + message: this.messages.duplicateMethodDecorators, + data: { decoratorName }, + }); + }); + } + + private reportMethodDuplicateDecorator( + annotation: arkts.AnnotationUsage, + decoratorName: string): void { + this.report({ + node: annotation, + message: this.messages.duplicateMethodDecorators, + data: { decoratorName }, + fix: () => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } +}; + +export default NoDuplicateDecoratorsRule; \ 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 new file mode 100644 index 0000000000000000000000000000000000000000..41e0a71e2ccc0d087652be5e19080dc88b6f6315 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.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 { getAnnotationUsage, MAX_ENTRY_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class NoDuplicateEntryRule extends AbstractUISyntaxRule { + private entryDecoratorUsages: arkts.AnnotationUsage[] = []; + private entryDecoratorUsageIndex = 1; + + public setup(): Record { + return { + duplicateEntry: `A page can't contain more then one '@Entry' decorator.`, + }; + } + + 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)!; + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + } + entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + this.entryDecoratorUsageIndex++; + } +} + +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..5b0470e8efa6a955cb48cadeab61a6c82327f97d --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +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 idInfos = this.getAllIdParams(node, []); + for (const idInfo of idInfos) { + const value = idInfo.value; + const idNode = idInfo.node; + if (usedIds.has(value)) { + this.report({ + node: idNode, + message: this.messages.duplicateId, + data: { + id: idInfo.value, + path: this.getPath() ?? '', + line: idNode.startPosition.line().toString(), + index: idNode.startPosition.index().toString() + } + }); + } else { + // Otherwise, record it + usedIds.set(value, idInfo); + } + } + } + + private getPath(): string | undefined { + const contextPtr = arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + return program.globalAbsName; + } + return undefined; + } + + 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 }; + } + + private getAllIdParams(node: arkts.AstNode, results: IdInfo[]): IdInfo[] { + if (arkts.isCallExpression(node)) { + const idInfo = this.getIdInfo(node); + if (idInfo) { + results.push(idInfo); + } + + // Just continue to check if the callee that calls the expression is a chain call (e.g. with .id()) + const callee = node.expression; + if (arkts.isMemberExpression(callee)) { + const object = callee.object; + if (arkts.isCallExpression(object)) { + this.getAllIdParams(object, results); + } + } + } + + return results.sort((a, b) => { + return a.node.startPosition.index() - b.node.startPosition.index(); + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..35326c6d090e0ce6e0512d65498819960e6c2366 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, MAX_PREVIEW_DECORATOR_COUNT, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class NoDuplicatePreviewRule extends AbstractUISyntaxRule { + private previewDecoratorUsages: arkts.AnnotationUsage[] = []; + private previewDecoratorUsageIndex = 10; + + public setup(): Record { + return { + duplicateEntry: `A page can contain at most 10 '@Preview' decorators.`, + }; + } + + 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 { + this.reportError(this.previewDecoratorUsages.at(this.previewDecoratorUsageIndex)!); + } + this.previewDecoratorUsageIndex++; + } + + private reportError(errorNode: arkts.AnnotationUsage): void { + this.report({ + node: errorNode, + message: this.messages.duplicateEntry, + fix: () => { + return { + range: [errorNode.startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..e41aebcfcceefbec350f6356620a593e259f5288 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-state-manager.ts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getIdentifierName, PresetDecorators, getAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const stateManagementDecorator = { + STATE: PresetDecorators.STATE, + PROP: PresetDecorators.PROP, + LINK: PresetDecorators.LINK, + PROVIDE: PresetDecorators.PROVIDER, + CONSUME: PresetDecorators.CONSUME +}; + +const CLASS_PROPERTY_ANNOTATION_ONE: number = 1; + +class NoDuplicateStateManagerRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + duplicateState: `The property '{{attributeName}}' cannot have multiple state management decorators.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + + // If it's a struct for @ComponentV2, the check is skipped + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + if (componentV2Decorator) { + return; + } + + this.checkForDuplicateStateDecorators(node); + } + + // Check that the properties in the struct are not reused with state management-related decorators + private checkForDuplicateStateDecorators(node: arkts.StructDeclaration): void { + node.definition.body.forEach((body) => { + if (!arkts.isClassProperty(body)) { + return; + } + + 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; + if (!propertyNameNode || !arkts.isIdentifier(propertyNameNode)) { + return; + } + + const attributeName = getIdentifierName(propertyNameNode); + if (stateDecorators.length > CLASS_PROPERTY_ANNOTATION_ONE) { + this.report({ + node: propertyNameNode, + message: this.messages.duplicateState, + data: { attributeName }, + }); + } + }); + } +} + +export default NoDuplicateStateManagerRule; 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 new file mode 100644 index 0000000000000000000000000000000000000000..b9f7772e65a909f06cefea04aa383d1b46771e4c --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.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 { getAnnotationUsage, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const invalidDecorators = [PresetDecorators.PROP, PresetDecorators.LINK, PresetDecorators.OBJECT_LINK]; + +class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + disallowDecoratorInEntry: `The '@Entry' component '{{componentName}}' cannot have the '@{{decoratorName}}' property '{{propertyName}}'.`, + }; + } + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + 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; + } + 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) => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + } +} + +export default NoPropLinkObjectLinkInEntryRule; \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/ETSLaunchExpression.ts b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts similarity index 31% rename from koala-wrapper/src/generated/peers/ETSLaunchExpression.ts rename to arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts index 39a42bcc69839a1b73a3b9b4555422e886a86a30..5940191f68b5f240a1b74058874dd0a235291b64 100644 --- a/koala-wrapper/src/generated/peers/ETSLaunchExpression.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts @@ -13,43 +13,39 @@ * limitations under the License. */ -import { - global, - passNode, - passNodeArray, - unpackNonNullableNode, - unpackNode, - unpackNodeArray, - assertValidPeer, - AstNode, - Es2pandaAstNodeType, - KNativePointer, - nodeByType, - ArktsObject, - unpackString -} from "../../reexport-for-generated" +import * as arkts from '@koalaui/libarkts'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; +import { isBuiltInAttribute } from '../utils'; -import { Expression } from "./Expression" -import { CallExpression } from "./CallExpression" -export class ETSLaunchExpression extends Expression { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 75) - super(pointer) - +class NoSameAsBuiltInAttributeRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + 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; } - static createETSLaunchExpression(expr?: CallExpression): ETSLaunchExpression { - return new ETSLaunchExpression(global.generatedEs2panda._CreateETSLaunchExpression(global.context, passNode(expr))) + if (!node.definition) { + return; } - static updateETSLaunchExpression(original?: ETSLaunchExpression, expr?: CallExpression): ETSLaunchExpression { - return new ETSLaunchExpression(global.generatedEs2panda._UpdateETSLaunchExpression(global.context, passNode(original), passNode(expr))) + if (!arkts.isClassDefinition(node.definition)) { + return; } - get call(): CallExpression | undefined { - return unpackNode(global.generatedEs2panda._ETSLaunchExpressionCallConst(global.context, this.peer)) + 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 function isETSLaunchExpression(node: AstNode): node is ETSLaunchExpression { - return node instanceof ETSLaunchExpression -} -if (!nodeByType.has(75)) { - nodeByType.set(75, ETSLaunchExpression) -} \ No newline at end of file + } +}; + +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 new file mode 100644 index 0000000000000000000000000000000000000000..b18d51a2ff39f3f4fb84c2ebfb513bd40d4fd205 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { getIdentifierName, PresetDecorators } from '../utils/index'; + +class ObservedHeritageCompatibleCheckRule extends AbstractUISyntaxRule { + // Record the class name decorated with @Observed and @ObservedV2 + private observedClasses: string[] = []; + private observedV2Classes: string[] = []; + + 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'.`, + }; + } + + public beforeTransform(): void { + this.observedClasses = []; + this.observedV2Classes = []; + } + + 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); + } + + 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); + } + // If there is a @ObservedV2 decorator, record the class name + if (hasObservedV2Decorator) { + const className = child.definition?.ident?.name ?? ''; + observedV2Classes.push(className); + } + } + } + } + + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..79d42a4d670b9a99bb6626fd74100fa3b9ab64ad --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.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 { PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ObservedObservedV2Rule extends AbstractUISyntaxRule { + public setup(): Record { + return { + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..1185160a5bc079358af74e5dca932b9b2742b550 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, findDecorator, getClassDeclarationAnnotation +} from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + observedV2DecoratorError: `The '@ObservedV2' decorator can only be used in 'class'.`, + traceDecoratorError: `The '@Trace' decorator can only be used in 'class'.`, + traceMemberVariableError: `The '@Trace' decorator 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' decorator can only be used within a 'class' decorated with 'ObservedV2'.`, + traceMustUsedWithObservedV2Update: `The '@Trace' decorator can only be used within a 'class' decorated with 'ObservedV2'.`, + }; + } + + public parsed(node: arkts.AstNode): void { + this.validateTraceDecoratorUsage(node); + } + + private getObservedDecorator(node: arkts.ClassDeclaration): arkts.AnnotationUsage | undefined { + if (!node.definition) { + return undefined; + } + return getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V1); + } + + private reportObservedV2DecoratorError(observedV2Decorator: arkts.AnnotationUsage) + : void { + this.report({ + node: observedV2Decorator, + message: this.messages.observedV2DecoratorError, + fix: (observedV2Decorator) => { + const startPosition = observedV2Decorator.startPosition; + const endPosition = observedV2Decorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private reportTraceMemberVariableError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceMemberVariableError, + fix: (traceDecorator) => { + const startPosition = traceDecorator.startPosition; + const endPosition = traceDecorator.endPosition; + return { + 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); + } + } + } + + private reportTraceDecoratorError(traceDecorator: arkts.AnnotationUsage) + : void { + this.report({ + node: traceDecorator, + message: this.messages.traceDecoratorError, + fix: (traceDecorator) => { + const startPosition = traceDecorator.startPosition; + const endPosition = traceDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private reportTraceMustUsedWithObservedV2(traceDecorator: arkts.AnnotationUsage, + currentNode: arkts.ClassDeclaration): void { + this.report({ + node: traceDecorator, + message: this.messages.traceMustUsedWithObservedV2, + fix: () => { + const startPosition = currentNode.startPosition; + return { + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } + + 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 { + range: [startPosition, endPosition], + code: `@${PresetDecorators.OBSERVED_V2}`, + }; + }, + }); + } + + 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); + } + } + } + + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..478ac72c0e4282ba5fc82c18ade880481fb706a3 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getAnnotationName, getAnnotationUsage, getIdentifierName } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { + private static readonly oldV1Decorators: string[] = [ + PresetDecorators.STATE, + PresetDecorators.PROP, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.WATCH, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + PresetDecorators.OBJECT_LINK, + ]; + + private static readonly newV2decorators: string[] = [ + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.ONCE, + PresetDecorators.EVENT, + PresetDecorators.MONITOR, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.COMPUTED, + ]; + + public setup(): Record { + return { + oldAndNewDecoratorsMixUse: `The '@{{decoratorName}}' decorator 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); + } + }); + } + + 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); + this.report({ + node: errorDecorator, + message: this.messages.oldAndNewDecoratorsMixUse, + data: { + decoratorName: propertyDecoratorName, + component: structDecoratorName, + }, + fix: () => { + return { + range: [hasComponentV2Decorator.startPosition, hasComponentV2Decorator.endPosition], + code: `@${structDecoratorName}`, + }; + }, + }); + } + + 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 { + range: [structNode.startPosition, structNode.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..1625f32ad9f69ee68f141be830d93ee95642baa3 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getClassPropertyAnnotationNames, PresetDecorators, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class OnceDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidUsage: `The '@Once' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, + invalidMemberDecorate: `'@Once' can only decorate member property.`, + invalidDecorator: `When a variable decorated with '@Once', it must also be decorated with '@Param'.`, + invalidNOtInStruct: `'@Once' decorator can only be used with 'struct'.` + }; + } + + public parsed(node: arkts.AstNode): void { + let onceDecorator: arkts.AnnotationUsage | undefined; + + this.validateOnlyInStruct(node); + this.validateOnlyOnProperty(node); + + if (arkts.isStructDeclaration(node)) { + // Check if the struct is decorated with @ComponentV2 + const componentV2DecoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecoratorUsage = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + this.validateDecorator(node, onceDecorator, componentV2DecoratorUsage, componentDecoratorUsage); + } + } + + 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; + } + } + + 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; + } + + this.report({ + node: decorator, + message: message, + fix: (decorator) => { + const startPosition = decorator.startPosition; + const endPosition = decorator.endPosition; + return { + 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); + } + } + + private validateDecorator( + node: arkts.StructDeclaration, + onceDecorator: arkts.AnnotationUsage | undefined, + componentV2DecoratorUsage: arkts.AnnotationUsage | undefined, + componentDecoratorUsage: arkts.AnnotationUsage | undefined, + ): 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, onceDecorator); + // If @Once is used but not in a @ComponentV2 struct, report an error + this.invalidComponentUsage(node, body, onceDecorator, componentV2DecoratorUsage, componentDecoratorUsage); + } + }); + } + + private validatePropertyAnnotations( + body: arkts.ClassProperty, + onceDecorator: arkts.AnnotationUsage | undefined + ): void { + const propertyAnnotations = getClassPropertyAnnotationNames(body); + onceDecorator = findDecorator(body, PresetDecorators.ONCE); + if (onceDecorator) { + const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); + // If @Once is found, check if @Param is also used + if (!isParamUsed) { + this.reportMissingParamWithOnce(onceDecorator); + } else { + // If both @Once and @Param are used, check for other + // incompatible decorators + const otherDecorators = body.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.ONCE && + annotation.expr.name !== PresetDecorators.PARAM + ); + this.reportInvalidDecoratorsWithOnceAndParam(otherDecorators); + } + } + } + + private invalidComponentUsage( + node: arkts.StructDeclaration, + body: arkts.ClassProperty, + onceDecorator: arkts.AnnotationUsage | undefined, + componentV2DecoratorUsage: arkts.AnnotationUsage | undefined, + componentDecoratorUsage: arkts.AnnotationUsage | undefined, + ): void { + onceDecorator = findDecorator(body, PresetDecorators.ONCE); + if (onceDecorator && !componentV2DecoratorUsage) { + this.reportInvalidOnceUsage(onceDecorator, node, componentDecoratorUsage); + } + } + + private reportMissingParamWithOnce(onceDecorator: arkts.AnnotationUsage | undefined): void { + if (!onceDecorator) { + return; + } + this.report({ + node: onceDecorator, + message: this.messages.invalidDecorator, + fix: (onceDecorator) => { + const startPosition = onceDecorator.endPosition; + const endPosition = onceDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: `@${PresetDecorators.PARAM}` + }; + } + }); + } + + private reportInvalidDecoratorsWithOnceAndParam(otherDecorators: arkts.AnnotationUsage | undefined): void { + if (!otherDecorators) { + return; + } + this.report({ + node: otherDecorators, + message: this.messages.invalidDecorator, + fix: (otherDecorators) => { + const startPosition = otherDecorators.startPosition; + const endPosition = otherDecorators.endPosition; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportInvalidOnceUsage( + onceDecorator: arkts.AnnotationUsage | undefined, + node: arkts.StructDeclaration, + componentDecoratorUsage: arkts.AnnotationUsage | undefined, + ): void { + if (!onceDecorator) { + return; + } + this.report({ + node: onceDecorator, + message: this.messages.invalidUsage, + fix: () => { + if (componentDecoratorUsage) { + const startPosition = componentDecoratorUsage.startPosition; + const endPosition = componentDecoratorUsage.endPosition; + return { + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}` + }; + } else { + const startPosition = node.startPosition; + const endPosition = node.startPosition; + return { + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n` + }; + } + } + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..f31cfec4ad1e92cf52c584d8b9562a9f4d741bf1 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const allowedDecorators = PresetDecorators.BUILDER; +const PARAM_THIS_NAME = '=t'; +const DECORATOR_LIMIT = 1; + +class OneDecoratorOnFunctionMethodRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidDecorator: `A function can only be decorated by the 'Builder'.`, + }; + } + + public parsed(node: arkts.AstNode): void { + // If the node is not an ETS script, it is returned directly + if (!arkts.isEtsScript(node)) { + 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; + } + this.report({ + node: annotation, + message: this.messages.invalidDecorator, + fix: () => { + const startPosition = otherDecorator.startPosition; + const endPosition = otherDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: `` + }; + } + }); + } +} + +export default OneDecoratorOnFunctionMethodRule; \ 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..f761fba6b7aaabaca44de159c18d427ba17b9689 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/require-decorator-regular.ts @@ -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 * as arkts from '@koalaui/libarkts'; +import { getClassPropertyAnnotationNames, PresetDecorators, isPrivateClassProperty, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const allowedDecorators = [ + PresetDecorators.STATE, + PresetDecorators.PROVIDE, + PresetDecorators.PROP, + PresetDecorators.PARAM, + PresetDecorators.BUILDER_PARAM +]; + +class RequireDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidUsage: `The @Require decorator can only be used on a regular variable or a variable decorated by @State, @Provide, @Prop, @Param, or @BuilderParam.`, + 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); + } + // Filter the decorators to find any that are not allowed with @Require + const otherDecorator = this.findConflictingDecorator(member, allowedDecorators); + const requireDecorator = findDecorator(member, PresetDecorators.REQUIRE); + if (otherDecorator && requireDecorator) { + this.report({ + node: requireDecorator, + message: this.messages.invalidUsage, + }); + } + }); + } + + private findConflictingDecorator( + member: arkts.ClassProperty, + allowedDecorators: string[] + ): arkts.AnnotationUsage | undefined { + return member.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.REQUIRE && + !allowedDecorators.includes(annotation.expr.name) + ); + } + + 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..31a38e4b2c175d0c186a27d3399243c71298605b --- /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)) { + return; + } + 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..03d65bdd221b98d934fc70b21486ab594bb5510e --- /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' decorators 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 new file mode 100644 index 0000000000000000000000000000000000000000..5c60e83b6f6da95069420fdb7557e2027c835468 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, getAnnotationUsage, ReuseConstants } from '../utils'; + +class ReuseAttributeCheckRule extends AbstractUISyntaxRule { + private reusableV2ComponentV2Struct: string[] = []; + + 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.`, + }; + } + + public beforeTransform(): void { + this.reusableV2ComponentV2Struct = []; + } + + public parsed(node: arkts.AstNode): void { + // Check whether the type is "Program" + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + this.findStructsWithReusableAndComponentV2(node); + } + if (arkts.isMemberExpression(node)) { + 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 { + 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 { + range: [startPosition, endPosition], + code: ReuseConstants.REUSE, + }; + }, + }); + } +}; + +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/struct-missing-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a81d555cf522be2864e4cdb4c18a86518b3e0e3 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-missing-decorator.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class StructMissingDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + missingComponentDecorator: `Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '{{structName}}'.` + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + 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 = 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) { + this.report({ + node: structNode, + message: this.messages.missingComponentDecorator, + data: { structName }, + }); + } + } + + private hasDecorator(node: arkts.StructDeclaration, decorator: string): boolean { + return !!getAnnotationUsage(node, decorator); + } +} + +export default StructMissingDecoratorRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts new file mode 100644 index 0000000000000000000000000000000000000000..12945ee48719f9628d9f606b52d2483e71268369 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-no-extends.ts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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'; + +class StructNoExtendsRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + structNoExtends: `Structs are not allowed to inherit from classes or implement interfaces.`, + }; + } + 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, + }); + } + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..9831b0d24c5a4ec1da5b010e9e34de7759979096 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.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'; +import { getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const decorators: string[] = [ + PresetDecorators.BUILDER_PARAM, + PresetDecorators.STATE, + PresetDecorators.PROP, + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.STORAGE_LINK, + PresetDecorators.WATCH, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + PresetDecorators.REQUIRE, +]; + +class StructPropertyDecoratorRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidStaticUsage: `The static variable of struct cannot be used together with built-in decorators.` + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + this.checkInvalidStaticPropertyDecorations(node); + } + + private hasPropertyDecorator( + member: arkts.ClassProperty, + ): boolean { + const annotationName = getClassPropertyAnnotationNames(member); + return decorators.some(decorator => + annotationName.includes(decorator) + ); + } + + private checkInvalidStaticPropertyDecorations(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 && this.hasPropertyDecorator(member)) && propertyNameNode) { + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); + } + } + }); + } +} + +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..e84a63d7ab3ad7ff2607578c722dcc5f8aad5a48 --- /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 && 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 new file mode 100644 index 0000000000000000000000000000000000000000..325f64266e0570ba480a365b8a5f268adac725db --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, findDecorator } from '../utils'; + +// A list of decorators that needs to be initialized locally +const mustInitializeDecorators = [ + PresetDecorators.STATE, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + PresetDecorators.PROVIDE, +]; +// Disables a list of decorators that are initialized locally +const prohibitInitializeDecorators = [ + PresetDecorators.LINK, + PresetDecorators.OBJECT_LINK, +]; + +// 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.` + }; + } + + public parsed(node: arkts.AstNode): void { + // Check if the current node is a class attribute + if (!arkts.isClassProperty(node)) { + 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 }, + }); + } + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..1cf71f9f1095d92e53b0334087826f33c71453d9 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.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 { PresetDecorators, findDecorator, getClassDeclarationAnnotation } from '../utils/index'; + +class TrackDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + trackOnClassMemberOnly: `The '@Track' decorator can decorate only member variables of a class.`, + trackMustUsedWithObserved: `'@Track' cannot be used with classes decorated by '@ObservedV2'. Use the '@Trace' decorator 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)) { + this.checkInvalidTrackAnnotations(node); + } + // Check if the current node is a class declaration + if (arkts.isClassDeclaration(node)) { + this.checkTrackUsedWithObservedV2(node); + } + } + + 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); + } + } + + private checkInvalidTrackAnnotations(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 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); + } + } + },); + } + + private checkTrackUsedWithObservedV2(node: arkts.ClassDeclaration): void { + // Check if the class is decorated with @Observed + const observedV2Decorator = getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V2); + // 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); + } + } + }); + } + + private reportInvalidClass(trackDecorator: arkts.AnnotationUsage): void { + this.report({ + node: trackDecorator, + message: this.messages.trackMustUsedWithObserved, + fix: (trackDecorator) => { + const startPosition = trackDecorator.startPosition; + const endPosition = trackDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + + private reportInvalidTarget(node: arkts.AnnotationUsage): void { + this.report({ + node: node, + message: this.messages.trackOnClassMemberOnly, + fix: (node) => { + const startPosition = node.startPosition; + const endPosition = node.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..fe9556e3c4a7f3461192de2c53416d2669958626 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/type-decorator-check.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 arkts from '@koalaui/libarkts'; +import { getAnnotationName, PresetDecorators, findDecorator, getClassDeclarationAnnotation } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class TypeDecoratorCheckRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidType: `The @Type decorator is not allowed here. It must be used in a class.`, + invalidDecoratorWith: `The @Type decorator can not be used within a 'class' decorated with @Observed.`, + invalidTypeMember: `The @Type decorator is not allowed here. It can only decorate properties of a class.` + }; + } + + public parsed(node: arkts.AstNode): void { + + this.checkTypeOnlyForClass(node); + + // Check the decorator on the class + if (arkts.isClassDeclaration(node)) { + this.checkObservedAndTypeConflict(node); + } + + if (arkts.isScriptFunction(node)) { + this.validateScriptFunctionForTypeDecorator(node); + } + } + + // rule1: @Type can only be used for class + private checkTypeOnlyForClass(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateDecorator(member, PresetDecorators.TYPE); + } + if (arkts.isMethodDefinition(member)) { + this.validateDecorator(member.scriptFunction, PresetDecorators.TYPE); + } + }); + return; + } + + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateDecorator(node, PresetDecorators.TYPE); + } + } + + // rule2: Conflict between @Type and @Observed + private checkObservedAndTypeConflict( + node: arkts.ClassDeclaration, + ): void { + let typeDecorator: arkts.AnnotationUsage | undefined; + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + typeDecorator = findDecorator(member, PresetDecorators.TYPE); + } + }); + const annotation = getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V1); + if (typeDecorator && annotation) { + this.reportObservedAndTypeDecoratorConflict(typeDecorator); + } + } + + // rule3: @TypeCannot be used for function members + private validateScriptFunctionForTypeDecorator( + node: arkts.ScriptFunction, + ): void { + const typeDecorator = findDecorator(node, PresetDecorators.TYPE); + this.reportInvalidTypeDecorator(typeDecorator); + } + + private validateDecorator( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string + ): void { + const decorator = findDecorator(node, decoratorName); + if (!decorator) { + return; + } + + this.report({ + node: decorator, + message: this.messages.invalidType, + fix: (decorator) => { + const startPosition = decorator.startPosition; + const endPosition = decorator.endPosition; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportObservedAndTypeDecoratorConflict( + typeDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!typeDecorator) { + return; + } + this.report({ + node: typeDecorator, + message: this.messages.invalidDecoratorWith, + fix: () => { + const startPosition = typeDecorator.startPosition; + const endPosition = typeDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportInvalidTypeDecorator( + typeDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!typeDecorator) { + return; + } + this.report({ + node: typeDecorator, + message: this.messages.invalidTypeMember, + fix: (typeDecorator) => { + const startPosition = typeDecorator.startPosition; + const endPosition = typeDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } +} + +export default TypeDecoratorCheckRule; \ No newline at end of file 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..45d3705217922a485c22d72521651691c1b1c05e --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 mabe 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; + } + this.report({ + node: argNode, + message: this.messages.vpSizeWarning, + fix: () => { + return { + range: [argNode.startPosition, argNode.endPosition], + code: `$r('sys.float.${resources[0].resourceName}')`, + }; + } + }); + }); + } + + 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; + } + this.report({ + node: argNode, + message: this.messages.vpAndPxSizeWarning, + fix: () => { + return { + range: [argNode.startPosition, argNode.endPosition], + code: `$r('sys.float.${resources[0].resourceName}')`, + }; + } + }); + }); + } + + 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; + } + this.report({ + node: argNode, + message: this.messages.colorConsistentWarning, + fix: () => { + return { + range: [argNode.startPosition, argNode.endPosition], + code: `$r('sys.color.${resources[0].resourceName}')`, + }; + } + }); + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..77e44ebf5145c2aed9fba8739e41be43dbfa08e1 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { ProjectConfig } from 'common/plugin-context'; +import { UISyntaxRuleComponents } from 'ui-syntax-plugins/utils'; + +export type FixSuggestion = { + range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; + code: string; +}; + +export type ReportOptions = { + node: arkts.AstNode; + message: string; + data?: Record; + fix?: (node: arkts.AstNode) => FixSuggestion; + level?: UISyntaxRuleLevel; +}; + +export type UISyntaxRuleContext = { + projectConfig?: ProjectConfig; + componentsInfo: UISyntaxRuleComponents | undefined; + report(options: ReportOptions): void; + getMainPages(): string[]; +}; + +export type UISyntaxRulePhaseHandler = (node: arkts.AstNode) => void; + +export type UISyntaxRuleHandler = { + parsed?: UISyntaxRulePhaseHandler; + checked?: UISyntaxRulePhaseHandler; +}; + +export type UISyntaxRule = { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2a61378682f5d591f08094dae7218c9df2843cba --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.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 arkts from '@koalaui/libarkts'; +import { getIdentifierName, BUILD_NAME } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const NOT_PARAM_LENGTH: number = 0; +const BUILD_FUNCTION_COUNT_INI: number = 0; +const BUILD_FUNCTION_COUNT: number = 1; +const NOT_STATEMENT_LENGTH: number = 0; + +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.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; + this.validateBuild(node, buildFunctionCount); + } + + 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); + } + }); + } + + // 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); + } + }); + } + } + + 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 { + range: [startPosition, endPosition], + code: `` + }; + } + }); + } + } + + 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, blockStatement); + } + } + + // 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 { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private reportMissingBuildInStruct( + structName: arkts.Identifier | undefined, + blockStatement: arkts.BlockStatement, + ): void { + if (!structName) { + return; + } + this.report({ + node: structName, + message: this.messages.invalidBuild, + data: { + structName: getIdentifierName(structName), + }, + fix: () => { + const startPosition = blockStatement.startPosition; + const endPosition = startPosition; + return { + range: [startPosition, endPosition], + code: '{\nbuild() {\n}\n' + }; + } + }); + } +} + +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..e16040792dd860efe205b7df54fe6f048fa7c739 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-decorator-target.ts @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + PresetDecorators.LOCAL, + PresetDecorators.PARAM, + PresetDecorators.EVENT, + PresetDecorators.PROVIDER, + PresetDecorators.CONSUMER, + PresetDecorators.WATCH, + PresetDecorators.REQUIRE, + PresetDecorators.OBJECT_LINK, + PresetDecorators.TRACK, + PresetDecorators.ONCE +]; + +class ValidateDecoratorTargetRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + decoratorOnlyWithStruct: `The '@{{decoratorName}}' decorator 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-constructor.ts b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.ts new file mode 100644 index 0000000000000000000000000000000000000000..892e4cdf3e699386b185f5f6f7675bbe26760fb6 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/variable-initialization-via-component-constructor.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 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], + [PresetDecorators.REQUIRE, PresetDecorators.BUILDER_PARAM], + [PresetDecorators.REQUIRE, PresetDecorators.PARAM] + ]; + + private static readonly notAllowInitInConstructorDecorators: string[][] = [ + [PresetDecorators.STORAGE_LINK], + [PresetDecorators.STORAGE_PROP], + [PresetDecorators.CONSUME], + [PresetDecorators.LOCAL_STORAGE_LINK], + [PresetDecorators.LOCAL_STORAGE_PROP] + ]; + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..bd3ea010b327bf21467b8cea11d6175dcf2138d2 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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, isPrivateClassProperty, PresetDecorators, getClassPropertyName, findDecorator } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +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.`, + }; + } + + 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); + } + + 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 + } + + // 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; + } + + 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); + }); + } + + 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; + } + 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 { + 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 { + range: [startPosition, endPosition], + code: ``, + }; + }, + }); + } +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..fc2c30f3999f355d761f7a7d37193249fad67eab --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-regular.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 { findDecorator, getClassPropertyAnnotationNames, getClassPropertyName, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +const PROPERTY_ANNOTATION_NUM: number = 2; + +class WatchDecoratorRegularRule extends AbstractUISyntaxRule { + public setup(): Record { + return { + invalidWatch: `Regular variable '{{propertyName}}' can not be decorated with '@Watch'.`, + }; + } + + public parsed(node: arkts.AstNode): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..a8f2da56f9eebd4c64c0842de3d10a14da6bc8a5 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/wrap-builder-check.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 { getIdentifierName, PresetDecorators, WRAP_BUILDER, getFunctionAnnotationUsage } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class StructNoExtendsRule extends AbstractUISyntaxRule { + private builderFunctionNames: string[] = []; + + public setup(): Record { + return { + invalidWrapBuilderCheck: 'The wrapBuilder\'s parameter should be @Builder function.', + }; + } + + public beforeTransform(): void { + this.builderFunctionNames = []; + } + + public parsed(node: arkts.StructDeclaration): void { + this.collectBuilderFunctions(node); + this.validateWrapBuilderInIdentifier(node); + } + + // 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); + }); + } + + 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; + } + 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, + }); + } + } +} + +export default StructNoExtendsRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e86f815215f63a10f7e346303db0f5118a31509 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts @@ -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 * as arkts from '@koalaui/libarkts'; +import { UISyntaxLinterVisitor } from './ui-syntax-linter-visitor'; + +export class ParsedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..33e1c85f1e6dac6cf820c9b6d8673e2898532981 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts @@ -0,0 +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 * 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(); + } + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..cb9c231a55d23949ceefb206594272a82a5caaf4 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/utils/index.ts @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 * 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 = { + 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: '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', + 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', +}; + +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)) { + return ''; + } + return node.name; +} + +export function getAnnotationName(annotation: arkts.AnnotationUsage): string { + if (!annotation.expr) { + return ''; + } + return getIdentifierName(annotation.expr); +} + +export function getAnnotationUsage( + declaration: arkts.StructDeclaration, + annotationName: string +): arkts.AnnotationUsage | undefined { + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName + ); +} + +export function getClassAnnotationUsage( + 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)) + ); +} + +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 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 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 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 { + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC); +} + +export function isPrivateClassProperty(property: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE); +} + +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); + } +} + +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[]; + 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 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', + +}; diff --git a/koala-wrapper/BUILD.gn b/koala-wrapper/BUILD.gn index d9ad6989931697d9fd4d4cf1a2a1e6e643f09997..58807818d720d8eb681392d0927e90d37e035192 100644 --- a/koala-wrapper/BUILD.gn +++ b/koala-wrapper/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/ohos.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") npm_path = "//prebuilts/build-tools/common/nodejs/current/bin/npm" @@ -43,3 +44,11 @@ ohos_copy("ets2panda_koala_wrapper") { subsystem_name = "developtools" part_name = "ace_ets2bundle" } + +ohos_copy("ohos_ets_koala_wrapper") { + deps = [ ":gen_sdk_ts_wrapper" ] + sources = [ rebase_path("$target_gen_dir") ] + outputs = [ ohos_ets_koala_wrapper_path ] + subsystem_name = "developtools" + part_name = "ace_ets2bundle" +} \ No newline at end of file diff --git a/koala-wrapper/native/BUILD.gn b/koala-wrapper/native/BUILD.gn index 1f7e783fa9078f07ccbe90e7d1b4a32f15cc481a..f3055944c7e2f3b93da6e0141d921dc8f2b64016 100644 --- a/koala-wrapper/native/BUILD.gn +++ b/koala-wrapper/native/BUILD.gn @@ -26,6 +26,7 @@ shared_library("es2panda") { "./src/bridges.cc", "./src/common.cc", "./src/generated/bridges.cc", + "./src/memoryTracker.cc" ] include_dirs = [ diff --git a/koala-wrapper/native/include/common.h b/koala-wrapper/native/include/common.h index 77ed29cf834d22aadbe3e55db9dba3991170b1fe..399d28d2229fe63c5d7a8f730469ef5694ccb3a0 100644 --- a/koala-wrapper/native/include/common.h +++ b/koala-wrapper/native/include/common.h @@ -32,7 +32,7 @@ string getString(KStringPtr ptr); char* getStringCopy(KStringPtr& ptr); -inline KUInt unpackUInt(const KByte* bytes); +KUInt unpackUInt(const KByte* bytes); es2panda_ContextState intToState(KInt state); 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 5c177990edcaa7c876484d87a19bbfe7708a709c..3cc1282ac5484d50b0fedce2e75bcdf0bc97e244 100644 --- a/koala-wrapper/native/src/bridges.cc +++ b/koala-wrapper/native/src/bridges.cc @@ -15,6 +15,11 @@ #include "common.h" +#include +#include +#include +#include "memoryTracker.h" + KBoolean impl_ClassDefinitionIsFromStructConst(KNativePointer contextPtr, KNativePointer instancePtr) { auto context = reinterpret_cast(contextPtr); @@ -31,6 +36,22 @@ void impl_ClassDefinitionSetFromStructModifier(KNativePointer contextPtr, KNativ } KOALA_INTEROP_V2(ClassDefinitionSetFromStructModifier, KNativePointer, KNativePointer); +KBoolean impl_ImportSpecifierIsRemovableConst(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto node = reinterpret_cast(instancePtr); + return GetImpl()->ImportSpecifierIsRemovableConst(context, node); +} +KOALA_INTEROP_2(ImportSpecifierIsRemovableConst, KBoolean, KNativePointer, KNativePointer); + +void impl_ImportSpecifierSetRemovable(KNativePointer contextPtr, KNativePointer instancePtr) +{ + auto context = reinterpret_cast(contextPtr); + auto node = reinterpret_cast(instancePtr); + return GetImpl()->ImportSpecifierSetRemovable(context, node, true); +} +KOALA_INTEROP_V2(ImportSpecifierSetRemovable, KNativePointer, KNativePointer); + KNativePointer impl_AstNodeRecheck(KNativePointer contextPtr, KNativePointer nodePtr) { auto context = reinterpret_cast(contextPtr); @@ -162,7 +183,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); } @@ -205,6 +226,14 @@ KNativePointer impl_ContextErrorMessage(KNativePointer contextPtr) } KOALA_INTEROP_1(ContextErrorMessage, KNativePointer, KNativePointer) +KNativePointer impl_GetAllErrorMessages(KNativePointer contextPtr) +{ + auto context = reinterpret_cast(contextPtr); + + return new string(GetImpl()->GetAllErrorMessages(context)); +} +KOALA_INTEROP_1(GetAllErrorMessages, KNativePointer, KNativePointer) + KNativePointer impl_CallExpressionSignature(KNativePointer context, KNativePointer classInstance) { const auto _context = reinterpret_cast(context); @@ -236,12 +265,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); @@ -253,12 +301,35 @@ 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); +KNativePointer impl_CreateContextGenerateAbcForExternalSourceFiles( + KNativePointer configPtr, KInt fileNamesCount, KStringArray fileNames) +{ + auto config = reinterpret_cast(configPtr); + const std::size_t headerLen = 4; + const char **argv = + new const char *[static_cast(fileNamesCount)]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(fileNamesCount); ++i) { + strLen = unpackUInt(fileNames + position); + position += headerLen; + argv[i] = strdup(std::string(reinterpret_cast(fileNames + position), + strLen).c_str()); + position += strLen; + } + auto context = GetImpl()->CreateContextGenerateAbcForExternalSourceFiles( + config, fileNamesCount, argv); + delete[] argv; + return context; +} +KOALA_INTEROP_3(CreateContextGenerateAbcForExternalSourceFiles, KNativePointer, KNativePointer, KInt, KStringArray) + KBoolean impl_IsClassProperty(KNativePointer nodePtr) { auto node = reinterpret_cast(nodePtr); @@ -281,12 +352,20 @@ KBoolean impl_IsETSFunctionType(KNativePointer nodePtr) KOALA_INTEROP_1(IsETSFunctionType, KBoolean, KNativePointer) KInt impl_GenerateTsDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputDeclEts, KStringPtr &outputEts, - KBoolean exportAll) + KBoolean exportAll, KBoolean isolated) +{ + auto context = reinterpret_cast(contextPtr); + return GetImpl()->GenerateTsDeclarationsFromContext(context, outputDeclEts.data(), outputEts.data(), + exportAll, isolated); +} +KOALA_INTEROP_5(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KBoolean) + +KInt impl_GenerateStaticDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputPath) { auto context = reinterpret_cast(contextPtr); - return GetImpl()->GenerateTsDeclarationsFromContext(context, outputDeclEts.data(), outputEts.data(), exportAll); + return GetImpl()->GenerateStaticDeclarationsFromContext(context, outputPath.data()); } -KOALA_INTEROP_4(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean) +KOALA_INTEROP_2(GenerateStaticDeclarationsFromContext, KInt, KNativePointer, KStringPtr) void impl_InsertETSImportDeclarationAndParse(KNativePointer context, KNativePointer program, KNativePointer importDeclaration) @@ -364,10 +443,269 @@ 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); auto result = GetImpl()->ETSParserGetGlobalProgramAbsName(context); return new std::string(result); } -KOALA_INTEROP_1(ETSParserGetGlobalProgramAbsName, KNativePointer, KNativePointer) \ No newline at end of file +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); + const auto _classInstance = reinterpret_cast(classInstance); + auto _typedTsType = GetImpl()->TypedTsType(_context, _classInstance); + if (_typedTsType == nullptr) { + return nullptr; + } + const auto _instanceType = reinterpret_cast(_typedTsType); + auto _typeVar = GetImpl()->TypeVariable(_context, _instanceType); + if (_typeVar == nullptr) { + return nullptr; + } + const auto result = reinterpret_cast(GetImpl()->VariableDeclaration(_context, _typeVar)); + const auto declNode = GetImpl()->DeclNode(_context, result); + return declNode; +} +KOALA_INTEROP_2(ClassVariableDeclaration, KNativePointer, KNativePointer, KNativePointer) + +KBoolean impl_IsMethodDefinition(KNativePointer nodePtr) +{ + auto node = reinterpret_cast(nodePtr); + return GetImpl()->IsMethodDefinition(node); +} +KOALA_INTEROP_1(IsMethodDefinition, KBoolean, KNativePointer) + +KNativePointer impl_CreateETSImportDeclaration(KNativePointer context, KNativePointer source, + KNativePointerArray specifiers, KUInt specifiersSequenceLength, + KInt importKind, KNativePointer programPtr, KInt flags) +{ + const auto _context = reinterpret_cast(context); + const auto _source = reinterpret_cast(source); + const auto _specifiers = reinterpret_cast(specifiers); + const auto _specifiersSequenceLength = static_cast(specifiersSequenceLength); + const auto _importKind = static_cast(importKind); + const auto _program = reinterpret_cast(programPtr); + const auto _flags = static_cast(flags); + auto result = GetImpl()->ETSParserBuildImportDeclaration(_context, _importKind, _specifiers, + _specifiersSequenceLength, _source, _program, _flags); + return result; +} +KOALA_INTEROP_7(CreateETSImportDeclaration, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, + KUInt, KInt, KNativePointer, KInt) + +KNativePointer impl_AstNodeRangeConst(KNativePointer context, KNativePointer node) +{ + const auto _context = reinterpret_cast(context); + const auto _node = reinterpret_cast(node); + auto result = GetImpl()->AstNodeRangeConst(_context, _node); + return (void*)result; +} +KOALA_INTEROP_2(AstNodeRangeConst, KNativePointer, KNativePointer, KNativePointer) + +KNativePointer impl_SourceRangeStart(KNativePointer context, KNativePointer range) +{ + const auto _context = reinterpret_cast(context); + const auto _range = reinterpret_cast(range); + auto result = GetImpl()->SourceRangeStart(_context, _range); + return result; +} +KOALA_INTEROP_2(SourceRangeStart, KNativePointer, KNativePointer, KNativePointer) + +KNativePointer impl_SourceRangeEnd(KNativePointer context, KNativePointer range) +{ + const auto _context = reinterpret_cast(context); + const auto _range = reinterpret_cast(range); + auto result = GetImpl()->SourceRangeEnd(_context, _range); + return result; +} +KOALA_INTEROP_2(SourceRangeEnd, KNativePointer, KNativePointer, KNativePointer) +bool impl_ClassPropertyIsDefaultAccessModifierConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + return GetImpl()->ClassPropertyIsDefaultAccessModifierConst(_context, _receiver); +} +KOALA_INTEROP_2(ClassPropertyIsDefaultAccessModifierConst, KBoolean, KNativePointer, KNativePointer); + +KNativePointer impl_AstNodeStartConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + return const_cast(GetImpl()->AstNodeStartConst(_context, _receiver)); +} +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); + const auto _receiver = reinterpret_cast(receiver); + return const_cast(GetImpl()->AstNodeEndConst(_context, _receiver)); +} +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); + return GetImpl()->IsArrayExpression(node); +} +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; + const KUInt BYTE_1 = 1; + const KUInt BYTE_2 = 2; + const KUInt BYTE_3 = 3; + + const KUInt BYTE_1_SHIFT = 8; + const KUInt BYTE_2_SHIFT = 16; + const KUInt BYTE_3_SHIFT = 24; + return (bytes[BYTE_0] | (bytes[BYTE_1] << BYTE_1_SHIFT) + | (bytes[BYTE_2] << BYTE_2_SHIFT) | (bytes[BYTE_3] << BYTE_3_SHIFT) + ); +} + +KNativePointer impl_CreateDiagnosticInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, KInt argc) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + 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); +} +KOALA_INTEROP_4(CreateDiagnosticInfo, KNativePointer, KNativePointer, KNativePointer, KStringArray, KInt); + +KNativePointer impl_CreateSuggestionInfo(KNativePointer context, KNativePointer kind, KStringArray argsPtr, + KInt argc, KStringPtr& substitutionCode) +{ + const auto _context = reinterpret_cast(context); + const auto _kind = reinterpret_cast(kind); + 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); + return GetImpl()->CreateSuggestionInfo(_context, _kind, _args, argc, _substitutionCode); +} +KOALA_INTEROP_5(CreateSuggestionInfo, KNativePointer, KNativePointer, KNativePointer, + KStringArray, KInt, KStringPtr); + +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, KNativePointer range) +{ + const auto _context = reinterpret_cast(context); + const auto _diagnosticInfo = reinterpret_cast(diagnosticInfo); + const auto _suggestionInfo = reinterpret_cast(suggestionInfo); + const auto _range = reinterpret_cast(range); + GetImpl()->LogDiagnosticWithSuggestion(_context, _diagnosticInfo, _suggestionInfo, _range); +} +KOALA_INTEROP_V4(LogDiagnosticWithSuggestion, KNativePointer, 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); diff --git a/koala-wrapper/native/src/common.cc b/koala-wrapper/native/src/common.cc index eb7a6acb5a72bdb6aa5c096824a6603e6eecd0d3..1f4fee8aa699f7c52e5eb1a6c90ec2e806a575e7 100644 --- a/koala-wrapper/native/src/common.cc +++ b/koala-wrapper/native/src/common.cc @@ -40,16 +40,50 @@ 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 = "\\"; +#else + const char *SEPARATOR = "/"; +#endif +const char *LIB_DIR = "lib"; + +static std::string ES2PANDA_LIB_PATH; + +std::string joinPath(vector &paths) +{ + std::string res; + for (int i = 0; i < paths.size(); ++i) { + if (i == 0) { + res = paths[i]; + } else { + res += SEPARATOR + paths[i]; + } + } + return res; +} + +void impl_SetUpSoPath(KStringPtr &soPath) +{ + ES2PANDA_LIB_PATH = std::string(soPath.c_str()); +} +KOALA_INTEROP_V1(SetUpSoPath, KStringPtr); void* FindLibrary() { - std::string libraryName; + std::vector pathArray; char* envValue = getenv("PANDA_SDK_PATH"); if (envValue) { - libraryName = std::string(envValue) + ("/" PLUGIN_DIR "/lib/") + LIB_ES2PANDA_PUBLIC; + pathArray = {envValue, PLUGIN_DIR, LIB_DIR, LIB_ES2PANDA_PUBLIC}; + } else if (!ES2PANDA_LIB_PATH.empty()) { + pathArray = {ES2PANDA_LIB_PATH, LIB_DIR, LIB_ES2PANDA_PUBLIC}; } else { - libraryName = LIB_ES2PANDA_PUBLIC; + pathArray = {LIB_ES2PANDA_PUBLIC}; } - return loadLibrary(libraryName); + return loadLibrary(joinPath(pathArray)); } es2panda_Impl *GetImpl() { @@ -98,6 +132,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; @@ -170,8 +254,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) { @@ -206,7 +302,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); @@ -226,6 +322,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 f1ca609eb9fcf72d57454a47da0f1a32f188c716..69bac2b1914024241cace0c04e4574e51f6f711f 100644 --- a/koala-wrapper/native/src/generated/bridges.cc +++ b/koala-wrapper/native/src/generated/bridges.cc @@ -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); @@ -1713,15 +1695,6 @@ void impl_NamedTypeSetTypeParams(KNativePointer context, KNativePointer receiver } KOALA_INTEROP_V3(NamedTypeSetTypeParams, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_NumberLiteralStrConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->NumberLiteralStrConst(_context, _receiver); - return new std::string(result); -} -KOALA_INTEROP_2(NumberLiteralStrConst, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_CreateTSFunctionType(KNativePointer context, KNativePointer signature) { const auto _context = reinterpret_cast(context); @@ -2716,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); @@ -3260,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); @@ -3334,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); @@ -4690,6 +4625,58 @@ void impl_ETSTupleSetTypeAnnotationsList(KNativePointer context, KNativePointer } KOALA_INTEROP_V4(ETSTupleSetTypeAnnotationsList, KNativePointer, KNativePointer, KNativePointerArray, KUInt); +KNativePointer impl_CreateTryStatement(KNativePointer context, KNativePointer block, KNativePointerArray catchClauses, KUInt catchClausesSequenceLength, KNativePointer finalizer, KNativePointerArray finalizerInsertionsLabelPair, KUInt finalizerInsertionsLabelPairSequenceLength, KNativePointerArray finalizerInsertionsStatement, KUInt finalizerInsertionsStatementSequenceLength) +{ + const auto _context = reinterpret_cast(context); + const auto _block = reinterpret_cast(block); + const auto _catchClauses = reinterpret_cast(catchClauses); + const auto _catchClausesSequenceLength = static_cast(catchClausesSequenceLength); + const auto _finalizer = reinterpret_cast(finalizer); + const auto _finalizerInsertionsLabelPair = reinterpret_cast(finalizerInsertionsLabelPair); + const auto _finalizerInsertionsLabelPairSequenceLength = static_cast(finalizerInsertionsLabelPairSequenceLength); + const auto _finalizerInsertionsStatement = reinterpret_cast(finalizerInsertionsStatement); + const auto _finalizerInsertionsStatementSequenceLength = static_cast(finalizerInsertionsStatementSequenceLength); + auto result = GetImpl()->CreateTryStatement(_context, _block, _catchClauses, _catchClausesSequenceLength, _finalizer, _finalizerInsertionsLabelPair, _finalizerInsertionsLabelPairSequenceLength, _finalizerInsertionsStatement, _finalizerInsertionsStatementSequenceLength); + return result; +} +KOALA_INTEROP_9(CreateTryStatement, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointerArray, KUInt, KNativePointerArray, KUInt); + +KNativePointer impl_UpdateTryStatement(KNativePointer context, KNativePointer original, KNativePointer block, KNativePointerArray catchClauses, KUInt catchClausesSequenceLength, KNativePointer finalizer, KNativePointerArray finalizerInsertionsLabelPair, KUInt finalizerInsertionsLabelPairSequenceLength, KNativePointerArray finalizerInsertionsStatement, KUInt finalizerInsertionsStatementSequenceLength) +{ + const auto _context = reinterpret_cast(context); + const auto _original = reinterpret_cast(original); + const auto _block = reinterpret_cast(block); + const auto _catchClauses = reinterpret_cast(catchClauses); + const auto _catchClausesSequenceLength = static_cast(catchClausesSequenceLength); + const auto _finalizer = reinterpret_cast(finalizer); + const auto _finalizerInsertionsLabelPair = reinterpret_cast(finalizerInsertionsLabelPair); + const auto _finalizerInsertionsLabelPairSequenceLength = static_cast(finalizerInsertionsLabelPairSequenceLength); + const auto _finalizerInsertionsStatement = reinterpret_cast(finalizerInsertionsStatement); + const auto _finalizerInsertionsStatementSequenceLength = static_cast(finalizerInsertionsStatementSequenceLength); + auto result = GetImpl()->UpdateTryStatement(_context, _original, _block, _catchClauses, _catchClausesSequenceLength, _finalizer, _finalizerInsertionsLabelPair, _finalizerInsertionsLabelPairSequenceLength, _finalizerInsertionsStatement, _finalizerInsertionsStatementSequenceLength); + return result; +} +KOALA_INTEROP_10(UpdateTryStatement, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KNativePointer, KNativePointerArray, KUInt, KNativePointerArray, KUInt); + +KNativePointer impl_CreateTryStatement1(KNativePointer context, KNativePointer other) +{ + const auto _context = reinterpret_cast(context); + const auto _other = reinterpret_cast(other); + auto result = GetImpl()->CreateTryStatement1(_context, _other); + return result; +} +KOALA_INTEROP_2(CreateTryStatement1, KNativePointer, KNativePointer, KNativePointer); + +KNativePointer impl_UpdateTryStatement1(KNativePointer context, KNativePointer original, KNativePointer other) +{ + const auto _context = reinterpret_cast(context); + const auto _original = reinterpret_cast(original); + const auto _other = reinterpret_cast(other); + auto result = GetImpl()->UpdateTryStatement1(_context, _original, _other); + return result; +} +KOALA_INTEROP_3(UpdateTryStatement1, KNativePointer, KNativePointer, KNativePointer, KNativePointer); + KNativePointer impl_TryStatementFinallyBlockConst(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -6648,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); @@ -6731,18 +6708,6 @@ KNativePointer impl_UpdateETSPackageDeclaration(KNativePointer context, KNativeP } KOALA_INTEROP_3(UpdateETSPackageDeclaration, KNativePointer, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_CreateETSImportDeclaration(KNativePointer context, KNativePointer source , KNativePointerArray specifiers, KUInt specifiersSequenceLength, KInt importKind) -{ - const auto _context = reinterpret_cast(context); - const auto _source = reinterpret_cast(source); - const auto _specifiers = reinterpret_cast(specifiers); - const auto _specifiersSequenceLength = static_cast(specifiersSequenceLength); - const auto _importKind = static_cast(importKind); - auto result = GetImpl()->ETSParserBuildImportDeclaration(_context, _importKind, _specifiers, _specifiersSequenceLength, _source); - return result; -} -KOALA_INTEROP_5(CreateETSImportDeclaration, KNativePointer, KNativePointer, KNativePointer, KNativePointerArray, KUInt, KInt); - KNativePointer impl_UpdateETSImportDeclaration(KNativePointer context, KNativePointer original, KNativePointer source, KNativePointerArray specifiers, KUInt specifiersSequenceLength, KInt importKind) { const auto _context = reinterpret_cast(context); @@ -6756,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); @@ -7242,43 +7198,6 @@ KNativePointer impl_UpdateEmptyStatement(KNativePointer context, KNativePointer } KOALA_INTEROP_2(UpdateEmptyStatement, KNativePointer, KNativePointer, KNativePointer); -KNativePointer impl_CreateETSLaunchExpression(KNativePointer context, KNativePointer expr) -{ - const auto _context = reinterpret_cast(context); - const auto _expr = reinterpret_cast(expr); - auto result = GetImpl()->CreateETSLaunchExpression(_context, _expr); - return result; -} -KOALA_INTEROP_2(CreateETSLaunchExpression, KNativePointer, KNativePointer, KNativePointer); - -KNativePointer impl_UpdateETSLaunchExpression(KNativePointer context, KNativePointer original, KNativePointer expr) -{ - const auto _context = reinterpret_cast(context); - const auto _original = reinterpret_cast(original); - const auto _expr = reinterpret_cast(expr); - auto result = GetImpl()->UpdateETSLaunchExpression(_context, _original, _expr); - return result; -} -KOALA_INTEROP_3(UpdateETSLaunchExpression, KNativePointer, KNativePointer, KNativePointer, KNativePointer); - -KBoolean impl_ETSLaunchExpressionIsStaticCallConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSLaunchExpressionIsStaticCallConst(_context, _receiver); - return result; -} -KOALA_INTEROP_2(ETSLaunchExpressionIsStaticCallConst, KBoolean, KNativePointer, KNativePointer); - -KNativePointer impl_ETSLaunchExpressionCallConst(KNativePointer context, KNativePointer receiver) -{ - const auto _context = reinterpret_cast(context); - const auto _receiver = reinterpret_cast(receiver); - auto result = GetImpl()->ETSLaunchExpressionCallConst(_context, _receiver); - return (void*)result; -} -KOALA_INTEROP_2(ETSLaunchExpressionCallConst, KNativePointer, KNativePointer, KNativePointer); - KNativePointer impl_CreateWhileStatement(KNativePointer context, KNativePointer test, KNativePointer body) { const auto _context = reinterpret_cast(context); @@ -11327,6 +11246,19 @@ KNativePointer impl_CreateForUpdateStatement(KNativePointer context, KNativePoin } KOALA_INTEROP_5(CreateForUpdateStatement, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer); +KNativePointer impl_UpdateForUpdateStatement(KNativePointer context, KNativePointer original, KNativePointer init, KNativePointer test, KNativePointer update, KNativePointer body) +{ + const auto _context = reinterpret_cast(context); + const auto _original = reinterpret_cast(original); + const auto _init = reinterpret_cast(init); + const auto _test = reinterpret_cast(test); + const auto _update = reinterpret_cast(update); + const auto _body = reinterpret_cast(body); + auto result = GetImpl()->UpdateForUpdateStatement(_context, _original, _init, _test, _update, _body); + return result; +} +KOALA_INTEROP_6(UpdateForUpdateStatement, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer, KNativePointer); + KNativePointer impl_ForUpdateStatementInit(KNativePointer context, KNativePointer receiver) { const auto _context = reinterpret_cast(context); @@ -11761,4 +11693,3 @@ KNativePointer impl_CreateFunctionDecl(KNativePointer context, KStringPtr& name, return result; } KOALA_INTEROP_3(CreateFunctionDecl, KNativePointer, KNativePointer, KStringPtr, KNativePointer); - 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 1dff8be516773d276eb3edce6f91a9803156b9af..0000000000000000000000000000000000000000 --- a/koala-wrapper/package-lock.json +++ /dev/null @@ -1,2730 +0,0 @@ -{ - "name": "@koalaui/libarkts", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@koalaui/libarkts", - "version": "1.0.0", - "devDependencies": { - "@babel/cli": "7.20.7", - "@babel/core": "7.20.12", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/preset-env": "7.20.2", - "@babel/preset-typescript": "7.18.6", - "@babel/runtime": "7.20.13", - "@tsconfig/recommended": "1.0.8", - "@types/node": "^18.0.0", - "node-addon-api": "^8.3.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@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, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.8", - "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" - }, - "bin": { - "babel": "bin/babel.js", - "babel-external-helpers": "bin/babel-external-helpers.js" - }, - "engines": { - "node": ">=6.9.0" - }, - "optionalDependencies": { - "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", - "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", - "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", - "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.5", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", - "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", - "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@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, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@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, - "license": "MIT", - "optional": true - }, - "node_modules/@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, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "18.19.111", - "resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.111.tgz", - "integrity": "sha512-90sGdgA+QLJr1F9X79tQuEut0gEYIfkX9pydI4XGRgvFo9g2JWswefI+WUSUHPYVBHYSEfTEqBxA5hQvAZB3Mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/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, - "license": "ISC", - "optional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "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" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/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, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://repo.huaweicloud.com/repository/npm/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "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" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/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, - "license": "ISC", - "optional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://repo.huaweicloud.com/repository/npm/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "8.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/node-addon-api/-/node-addon-api-8.3.1.tgz", - "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/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, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://repo.huaweicloud.com/repository/npm/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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, - "license": "MIT", - "optional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/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, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/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, - "license": "MIT" - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/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, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/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, - "license": "ISC" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://repo.huaweicloud.com/repository/npm/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/koala-wrapper/src/Es2pandaEnums.ts b/koala-wrapper/src/Es2pandaEnums.ts index bf820a7590208c2ec3d2614fe537b0d2e0e68110..9bb06c7d6109ad32fa7b369f52a043114292b7f5 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, @@ -78,10 +79,12 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_SCRIPT_FUNCTION, AST_NODE_TYPE_SEQUENCE_EXPRESSION, AST_NODE_TYPE_STRING_LITERAL, + AST_NODE_TYPE_ETS_NON_NULLISH_TYPE, AST_NODE_TYPE_ETS_NULL_TYPE, 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, @@ -91,7 +94,6 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART, AST_NODE_TYPE_ETS_UNION_TYPE, AST_NODE_TYPE_ETS_KEYOF_TYPE, - AST_NODE_TYPE_ETS_LAUNCH_EXPRESSION, AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION, AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION, AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION, @@ -181,4 +183,10 @@ 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, } diff --git a/koala-wrapper/src/Es2pandaNativeModule.ts b/koala-wrapper/src/Es2pandaNativeModule.ts index 3f271657ae50c3dde4608ee723495ce387f2fea7..1fcdcdd4f3c5e30756d73a6a0318eb29505fbb38 100644 --- a/koala-wrapper/src/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/Es2pandaNativeModule.ts @@ -21,9 +21,11 @@ import { registerNativeModuleLibraryName, loadNativeModuleLibrary, KDouble, + KStringArrayPtr, } 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; @@ -78,6 +80,9 @@ export class Es2pandaNativeModule { _ContextErrorMessage(context: KPtr): KPtr { throw new Error('Not implemented'); } + _GetAllErrorMessages(context: KPtr): KPtr { + throw new Error('Not implemented'); + } _AstNodeChildren(context: KPtr, node: KPtr): KPtr { throw new Error('Not implemented'); } @@ -103,6 +108,10 @@ export class Es2pandaNativeModule { _CreateContextFromString(config: KPtr, source: String, filename: String): KPtr { throw new Error('Not implemented'); } + _CreateContextGenerateAbcForExternalSourceFiles(config: KPtr, fileCount: KInt, filenames: + string[]): KPtr { + throw new Error('Not implemented'); + } _CreateContextFromFile(config: KPtr, filename: String): KPtr { throw new Error('Not implemented'); } @@ -288,11 +297,13 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } _CreateETSImportDeclaration( - context: KPtr, - source: KPtr, - specifiers: KPtrArray, - specifiersLen: KInt, - importKind: KInt + context: KNativePointer, + importPath: KNativePointer, + specifiers: BigUint64Array, + specifiersSequenceLength: KInt, + importKind: KInt, + programPtr: KNativePointer, + flags: KInt ): KNativePointer { throw new Error('Not implemented'); } @@ -732,7 +743,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'); } @@ -741,11 +757,23 @@ 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 + exportAll: KBoolean, + isolated: KBoolean + ): KPtr { + throw new Error('Not implemented'); + } + + _GenerateStaticDeclarationsFromContext( + config: KPtr, + outputPath: String ): KPtr { throw new Error('Not implemented'); } @@ -791,9 +819,141 @@ 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'); + } + + _ImportSpecifierSetRemovable(context: KNativePointer, instance: KNativePointer): void { + throw new Error('Not implemented'); + } + + _ClassPropertyIsDefaultAccessModifierConst(context: KNativePointer, receiver: KNativePointer): boolean { + throw new Error('Not implemented'); + } + + _AstNodeStartConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { + 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'); + } + + _SourceRangeStart(context: KNativePointer, range: KNativePointer): KNativePointer { + throw new Error('CreateFunctionDecl was not overloaded by native module initialization'); + } + + _SourceRangeEnd(context: KNativePointer, range: KNativePointer): KNativePointer { + throw new Error('CreateFunctionDecl was not overloaded by native module initialization'); + } + + _CreateSourceRange(context: KNativePointer, start: KNativePointer, end: KNativePointer): KNativePointer { + throw new Error('CreateFunctionDecl was not overloaded by native module initialization'); + } + + _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): KNativePointer { + throw new Error('Not implemented'); + } + + _CreateSuggestionInfo(context: KNativePointer, kind: KNativePointer, args: string[], + argc: number, substitutionCode: string): 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, range?: 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'); + } } 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..a12afcba877e8284d8cf61eec1898067b3089889 100644 --- a/koala-wrapper/src/arkts-api/class-by-peer.ts +++ b/koala-wrapper/src/arkts-api/class-by-peer.ts @@ -26,7 +26,7 @@ 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 5283543acd525542459049dd6928125ce4ce51cf..c780fdbf9e9b760ce14b9536ad5b386725d3772d 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeFactory.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeFactory.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { updateNodeByNode } from "../utilities/private" +import { updateNodeByNode } from '../utilities/private'; import { ArrowFunctionExpression, AssignmentExpression, @@ -29,10 +29,10 @@ import { StructDeclaration, VariableDeclaration, VariableDeclarator, - ETSStringLiteralType -} from "../types" -import { MemberExpression } from "../to-be-generated/MemberExpression" -import { AstNode } from "../peers/AstNode" + ETSStringLiteralType, +} from '../types'; +import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { AstNode } from '../peers/AstNode'; import { AnnotationUsage, BinaryExpression, @@ -69,373 +69,540 @@ import { TSTypeAliasDeclaration, ChainExpression, BlockExpression, - ETSNewClassInstanceExpression -} 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" -import { updateMemberExpression } from "../node-utilities/MemberExpression" -import { updateFunctionDeclaration } from "../node-utilities/FunctionDeclaration" -import { updateBlockStatement } from "../node-utilities/BlockStatement" -import { updateArrowFunctionExpression } from "../node-utilities/ArrowFunctionExpression" -import { updateScriptFunction } from "../node-utilities/ScriptFunction" -import { updateStringLiteral } from "../node-utilities/StringLiteral" -import { updateNumberLiteral } from "../node-utilities/NumberLiteral" -import { updateETSParameterExpression } from "../node-utilities/ETSParameterExpression" -import { updateTSTypeParameter } from "../node-utilities/TSTypeParameter" -import { updateTSTypeParameterDeclaration } from "../node-utilities/TSTypeParameterDeclaration" -import { updateETSPrimitiveType } from "../node-utilities/ETSPrimitiveType" -import { updateETSTypeReference } from "../node-utilities/ETSTypeReference" -import { updateETSTypeReferencePart } from "../node-utilities/ETSTypeReferencePart" -import { updateETSImportDeclaration } from "../node-utilities/ETSImportDeclaration" -import { updateImportSpecifier } from "../node-utilities/ImportSpecifier" -import { updateVariableDeclaration } from "../node-utilities/VariableDeclaration" -import { updateVariableDeclarator } from "../node-utilities/VariableDeclarator" -import { updateETSUnionType } from "../node-utilities/ETSUnionType" -import { updateReturnStatement } from "../node-utilities/ReturnStatement" -import { updateIfStatement } from "../node-utilities/IfStatement" -import { updateBinaryExpression } from "../node-utilities/BinaryExpression" -import { updateClassDeclaration } from "../node-utilities/ClassDeclaration" -import { updateStructDeclaration } from "../node-utilities/StructDeclaration" -import { updateClassDefinition } from "../node-utilities/ClassDefinition" -import { updateClassProperty } from "../node-utilities/ClassProperty" -import { updateETSFunctionType } from "../node-utilities/ETSFunctionType" -import { updateFunctionExpression } from "../node-utilities/FunctionExpression" -import { updateMethodDefinition } from "../node-utilities/MethodDefinition" -import { updateSuperExpression } from "../node-utilities/SuperExpression" -import { updateTSTypeParameterInstantiation } from "../node-utilities/TSTypeParameterInstantiation" -import { updateTSInterfaceDeclaration } from "../node-utilities/TSInterfaceDeclaration" -import { updateTSInterfaceBody } from "../node-utilities/TSInterfaceBody" -import { updateUndefinedLiteral } from "../node-utilities/UndefinedLiteral" -import { updateAnnotationUsage } from "../node-utilities/AnnotationUsage" -import { updateAssignmentExpression } from "../node-utilities/AssignmentExpression" -import { updateETSUndefinedType } from "../node-utilities/ETSUndefinedType" -import { updateConditionalExpression } from "../node-utilities/ConditionalExpression" -import { updateTSAsExpression } from "../node-utilities/TSAsExpression" -import { updateThisExpression } from "../node-utilities/ThisExpression" -import { updateTSTypeAliasDeclaration } from "../node-utilities/TSTypeAliasDeclaration" -import { updateTSNonNullExpression } from "../node-utilities/TSNonNullExpression" -import { updateChainExpression } from "../node-utilities/ChainExpression" -import { updateBlockExpression } from "../node-utilities/BlockExpression" -import { updateNullLiteral } from "../node-utilities/NullLiteral" -import { updateETSNewClassInstanceExpression } from "../node-utilities/ETSNewClassInstanceExpression" + ETSNewClassInstanceExpression, + BooleanLiteral, + ObjectExpression, + Property, + TemplateLiteral, + ArrayExpression, + AnnotationDeclaration, + TryStatement, + TSClassImplements, + ForUpdateStatement, + ForInStatement, + ForOfStatement, +} 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'; +import { updateMemberExpression } from '../node-utilities/MemberExpression'; +import { updateFunctionDeclaration } from '../node-utilities/FunctionDeclaration'; +import { updateBlockStatement } from '../node-utilities/BlockStatement'; +import { updateArrowFunctionExpression } from '../node-utilities/ArrowFunctionExpression'; +import { updateScriptFunction } from '../node-utilities/ScriptFunction'; +import { updateStringLiteral } from '../node-utilities/StringLiteral'; +import { updateNumberLiteral } from '../node-utilities/NumberLiteral'; +import { updateETSParameterExpression } from '../node-utilities/ETSParameterExpression'; +import { updateTSTypeParameter } from '../node-utilities/TSTypeParameter'; +import { updateTSTypeParameterDeclaration } from '../node-utilities/TSTypeParameterDeclaration'; +import { updateETSPrimitiveType } from '../node-utilities/ETSPrimitiveType'; +import { updateETSTypeReference } from '../node-utilities/ETSTypeReference'; +import { updateETSTypeReferencePart } from '../node-utilities/ETSTypeReferencePart'; +import { updateETSImportDeclaration } from '../node-utilities/ETSImportDeclaration'; +import { updateImportSpecifier } from '../node-utilities/ImportSpecifier'; +import { updateVariableDeclaration } from '../node-utilities/VariableDeclaration'; +import { updateVariableDeclarator } from '../node-utilities/VariableDeclarator'; +import { updateETSUnionType } from '../node-utilities/ETSUnionType'; +import { updateReturnStatement } from '../node-utilities/ReturnStatement'; +import { updateIfStatement } from '../node-utilities/IfStatement'; +import { updateBinaryExpression } from '../node-utilities/BinaryExpression'; +import { updateClassDeclaration } from '../node-utilities/ClassDeclaration'; +import { updateStructDeclaration } from '../node-utilities/StructDeclaration'; +import { updateClassDefinition } from '../node-utilities/ClassDefinition'; +import { updateClassProperty } from '../node-utilities/ClassProperty'; +import { updateETSFunctionType } from '../node-utilities/ETSFunctionType'; +import { updateFunctionExpression } from '../node-utilities/FunctionExpression'; +import { updateMethodDefinition } from '../node-utilities/MethodDefinition'; +import { updateSuperExpression } from '../node-utilities/SuperExpression'; +import { updateTSTypeParameterInstantiation } from '../node-utilities/TSTypeParameterInstantiation'; +import { updateTSInterfaceDeclaration } from '../node-utilities/TSInterfaceDeclaration'; +import { updateTSInterfaceBody } from '../node-utilities/TSInterfaceBody'; +import { updateUndefinedLiteral } from '../node-utilities/UndefinedLiteral'; +import { updateAnnotationUsage, update1AnnotationUsage } from '../node-utilities/AnnotationUsage'; +import { updateAssignmentExpression } from '../node-utilities/AssignmentExpression'; +import { updateETSUndefinedType } from '../node-utilities/ETSUndefinedType'; +import { updateConditionalExpression } from '../node-utilities/ConditionalExpression'; +import { updateTSAsExpression } from '../node-utilities/TSAsExpression'; +import { updateThisExpression } from '../node-utilities/ThisExpression'; +import { updateTSTypeAliasDeclaration } from '../node-utilities/TSTypeAliasDeclaration'; +import { updateTSNonNullExpression } from '../node-utilities/TSNonNullExpression'; +import { updateChainExpression } from '../node-utilities/ChainExpression'; +import { updateBlockExpression } from '../node-utilities/BlockExpression'; +import { updateNullLiteral } from '../node-utilities/NullLiteral'; +import { updateETSNewClassInstanceExpression } from '../node-utilities/ETSNewClassInstanceExpression'; +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'; export const factory = { - get createIdentifier() { - return Identifier.create2Identifier + get createIdentifier(): (...args: Parameters) => Identifier { + return Identifier.create2Identifier; }, - get updateIdentifier() { - return updateIdentifier + get updateIdentifier(): (...args: Parameters) => Identifier { + return updateIdentifier; }, - get createCallExpression() { - return CallExpression.create + get createCallExpression(): (...args: Parameters) => CallExpression { + return CallExpression.create; }, - get updateCallExpression() { - return updateCallExpression + get updateCallExpression(): (...args: Parameters) => CallExpression { + return updateCallExpression; }, - get createExpressionStatement() { - return ExpressionStatement.create + get createExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return ExpressionStatement.create; }, - get updateExpressionStatement() { - return updateExpressionStatement + get updateExpressionStatement(): (...args: Parameters) => ExpressionStatement { + return updateExpressionStatement; }, - get createMemberExpression() { - return MemberExpression.create + get createMemberExpression(): (...args: Parameters) => MemberExpression { + return MemberExpression.create; }, - get updateMemberExpression() { - return updateMemberExpression + get updateMemberExpression(): (...args: Parameters) => MemberExpression { + return updateMemberExpression; }, - get createEtsScript() { - return EtsScript.createFromSource + get createEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.createFromSource; }, - get updateEtsScript() { - return EtsScript.updateByStatements + get updateEtsScript(): (...args: Parameters) => EtsScript { + return EtsScript.updateByStatements; }, - get createFunctionDeclaration() { - return FunctionDeclaration.create + get createFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return FunctionDeclaration.create; }, - get updateFunctionDeclaration() { - return updateFunctionDeclaration + get updateFunctionDeclaration(): (...args: Parameters) => FunctionDeclaration { + return updateFunctionDeclaration; }, - get createBlock() { - return BlockStatement.createBlockStatement + get createBlock(): (...args: Parameters) => BlockStatement { + return BlockStatement.createBlockStatement; }, - get updateBlock() { - return updateBlockStatement + get updateBlock(): (...args: Parameters) => BlockStatement { + return updateBlockStatement; }, - get createArrowFunction() { - return ArrowFunctionExpression.create + get createArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return ArrowFunctionExpression.create; }, - get updateArrowFunction() { - return updateArrowFunctionExpression + get updateArrowFunction(): (...args: Parameters) => ArrowFunctionExpression { + return updateArrowFunctionExpression; }, - get createScriptFunction() { - return ScriptFunction.createScriptFunction + get createScriptFunction(): (...args: Parameters) => ScriptFunction { + return ScriptFunction.createScriptFunction; }, - get updateScriptFunction() { - return updateScriptFunction + get updateScriptFunction(): (...args: Parameters) => ScriptFunction { + return updateScriptFunction; }, - get createStringLiteral() { - return StringLiteral.create1StringLiteral + get createStringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get updateStringLiteral() { - return updateStringLiteral + get updateStringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get create1StringLiteral() { - return StringLiteral.create1StringLiteral + get create1StringLiteral(): (...args: Parameters) => StringLiteral { + return StringLiteral.create1StringLiteral; }, - get update1StringLiteral() { - return updateStringLiteral + get update1StringLiteral(): (...args: Parameters) => StringLiteral { + return updateStringLiteral; }, - get createNumericLiteral() { - return NumberLiteral.create + get createNumericLiteral(): (...args: Parameters) => NumberLiteral { + return NumberLiteral.create; }, - get updateNumericLiteral() { - return updateNumberLiteral + get updateNumericLiteral(): (...args: Parameters) => NumberLiteral { + return updateNumberLiteral; }, - get createParameterDeclaration() { - return ETSParameterExpression.create + get createParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return ETSParameterExpression.create; }, - get updateParameterDeclaration() { - return updateETSParameterExpression + get updateParameterDeclaration(): ( + ...args: Parameters + ) => ETSParameterExpression { + return updateETSParameterExpression; }, - get createTypeParameter() { - return TSTypeParameter.createTSTypeParameter + get createTypeParameter(): (...args: Parameters) => TSTypeParameter { + return TSTypeParameter.createTSTypeParameter; }, - get updateTypeParameter() { - return updateTSTypeParameter + get updateTypeParameter(): (...args: Parameters) => TSTypeParameter { + return updateTSTypeParameter; }, - get createTypeParameterDeclaration() { - return TSTypeParameterDeclaration.createTSTypeParameterDeclaration + get createTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return TSTypeParameterDeclaration.createTSTypeParameterDeclaration; }, - get updateTypeParameterDeclaration() { - return updateTSTypeParameterDeclaration + get updateTypeParameterDeclaration(): ( + ...args: Parameters + ) => TSTypeParameterDeclaration { + return updateTSTypeParameterDeclaration; }, - get createPrimitiveType() { - return ETSPrimitiveType.createETSPrimitiveType + get createPrimitiveType(): ( + ...args: Parameters + ) => ETSPrimitiveType { + return ETSPrimitiveType.createETSPrimitiveType; }, - get updatePrimitiveType() { - return updateETSPrimitiveType + get updatePrimitiveType(): (...args: Parameters) => ETSPrimitiveType { + return updateETSPrimitiveType; }, - get createTypeReference() { - return ETSTypeReference.createETSTypeReference + get createTypeReference(): ( + ...args: Parameters + ) => ETSTypeReference { + return ETSTypeReference.createETSTypeReference; }, - get updateTypeReference() { - return updateETSTypeReference + get updateTypeReference(): (...args: Parameters) => ETSTypeReference { + return updateETSTypeReference; }, - get createTypeReferencePart() { - return ETSTypeReferencePart.createETSTypeReferencePart + get createTypeReferencePart(): ( + ...args: Parameters + ) => ETSTypeReferencePart { + return ETSTypeReferencePart.createETSTypeReferencePart; }, - get updateTypeReferencePart() { - return updateETSTypeReferencePart + get updateTypeReferencePart(): (...args: Parameters) => ETSTypeReferencePart { + return updateETSTypeReferencePart; }, - get createImportDeclaration() { - return ETSImportDeclaration.createETSImportDeclaration + get createImportDeclaration(): ( + ...args: Parameters + ) => ETSImportDeclaration { + return ETSImportDeclaration.createETSImportDeclaration; }, - get updateImportDeclaration() { - return updateETSImportDeclaration + get updateImportDeclaration(): (...args: Parameters) => ETSImportDeclaration { + return updateETSImportDeclaration; }, - get createImportSpecifier() { - return ImportSpecifier.createImportSpecifier + get createImportSpecifier(): ( + ...args: Parameters + ) => ImportSpecifier { + return ImportSpecifier.createImportSpecifier; }, - get updateImportSpecifier() { - return updateImportSpecifier + get updateImportSpecifier(): (...args: Parameters) => ImportSpecifier { + return updateImportSpecifier; }, - get createVariableDeclaration() { - return VariableDeclaration.create + get createVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return VariableDeclaration.create; }, - get updateVariableDeclaration() { - return updateVariableDeclaration + get updateVariableDeclaration(): (...args: Parameters) => VariableDeclaration { + return updateVariableDeclaration; }, - get createVariableDeclarator() { - return VariableDeclarator.create + get createVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return VariableDeclarator.create; }, - get updateVariableDeclarator() { - return updateVariableDeclarator + get updateVariableDeclarator(): (...args: Parameters) => VariableDeclarator { + return updateVariableDeclarator; }, - get createUnionType() { - return ETSUnionType.createETSUnionType + get createUnionType(): (...args: Parameters) => ETSUnionType { + return ETSUnionType.createETSUnionType; }, - get updateUnionType() { - return updateETSUnionType + get updateUnionType(): (...args: Parameters) => ETSUnionType { + return updateETSUnionType; }, - get createReturnStatement() { - return ReturnStatement.create1ReturnStatement + get createReturnStatement(): ( + ...args: Parameters + ) => ReturnStatement { + return ReturnStatement.create1ReturnStatement; }, - get updateReturnStatement() { - return updateReturnStatement + get updateReturnStatement(): (...args: Parameters) => ReturnStatement { + return updateReturnStatement; }, - get createIfStatement() { - return IfStatement.create + get createIfStatement(): (...args: Parameters) => IfStatement { + return IfStatement.create; }, - get updateIfStatement() { - return updateIfStatement + get updateIfStatement(): (...args: Parameters) => IfStatement { + return updateIfStatement; }, - get createBinaryExpression() { - return BinaryExpression.createBinaryExpression + get createBinaryExpression(): ( + ...args: Parameters + ) => BinaryExpression { + return BinaryExpression.createBinaryExpression; }, - get updateBinaryExpression() { - return updateBinaryExpression + get updateBinaryExpression(): (...args: Parameters) => BinaryExpression { + return updateBinaryExpression; }, - get createClassDeclaration() { - return ClassDeclaration.createClassDeclaration + get createClassDeclaration(): ( + ...args: Parameters + ) => ClassDeclaration { + return ClassDeclaration.createClassDeclaration; }, - get updateClassDeclaration() { - return updateClassDeclaration + get updateClassDeclaration(): (...args: Parameters) => ClassDeclaration { + return updateClassDeclaration; }, - get createStructDeclaration() { - return StructDeclaration.create + get createStructDeclaration(): (...args: Parameters) => StructDeclaration { + return StructDeclaration.create; }, - get updateStructDeclaration() { - return updateStructDeclaration + get updateStructDeclaration(): (...args: Parameters) => StructDeclaration { + return updateStructDeclaration; }, - get createClassDefinition() { - return ClassDefinition.createClassDefinition + get createClassDefinition(): ( + ...args: Parameters + ) => ClassDefinition { + return ClassDefinition.createClassDefinition; }, - get updateClassDefinition() { - return updateClassDefinition + get updateClassDefinition(): (...args: Parameters) => ClassDefinition { + return updateClassDefinition; }, - get createClassProperty() { - return ClassProperty.createClassProperty + get createClassProperty(): (...args: Parameters) => ClassProperty { + return ClassProperty.createClassProperty; }, - get updateClassProperty() { - return updateClassProperty + get updateClassProperty(): (...args: Parameters) => ClassProperty { + return updateClassProperty; }, - get createFunctionType() { - return ETSFunctionType.createETSFunctionType + get createFunctionType(): (...args: Parameters) => ETSFunctionType { + return ETSFunctionType.createETSFunctionType; }, - get updateFunctionType() { - return updateETSFunctionType + get updateFunctionType(): (...args: Parameters) => ETSFunctionType { + return updateETSFunctionType; }, - get createFunctionExpression() { - return FunctionExpression.create + get createFunctionExpression(): (...args: Parameters) => FunctionExpression { + return FunctionExpression.create; }, - get updateFunctionExpression() { - return updateFunctionExpression + get updateFunctionExpression(): (...args: Parameters) => FunctionExpression { + return updateFunctionExpression; }, - get createMethodDefinition() { - return MethodDefinition.create + get createMethodDefinition(): (...args: Parameters) => MethodDefinition { + return MethodDefinition.create; }, - get updateMethodDefinition() { - return updateMethodDefinition + get updateMethodDefinition(): (...args: Parameters) => MethodDefinition { + return updateMethodDefinition; }, - get createSuperExpression() { - return SuperExpression.createSuperExpression + get createSuperExpression(): ( + ...args: Parameters + ) => SuperExpression { + return SuperExpression.createSuperExpression; }, - get updateSuperExpression() { - return updateSuperExpression + get updateSuperExpression(): (...args: Parameters) => SuperExpression { + return updateSuperExpression; }, - get createTSTypeParameterInstantiation() { - return TSTypeParameterInstantiation.createTSTypeParameterInstantiation + get createTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return TSTypeParameterInstantiation.createTSTypeParameterInstantiation; }, - get updateTSTypeParameterInstantiation() { - return updateTSTypeParameterInstantiation + get updateTSTypeParameterInstantiation(): ( + ...args: Parameters + ) => TSTypeParameterInstantiation { + return updateTSTypeParameterInstantiation; }, - get createInterfaceDeclaration() { - return TSInterfaceDeclaration.createTSInterfaceDeclaration + get createInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return TSInterfaceDeclaration.createTSInterfaceDeclaration; }, - get updateInterfaceDeclaration() { - return updateTSInterfaceDeclaration + get updateInterfaceDeclaration(): ( + ...args: Parameters + ) => TSInterfaceDeclaration { + return updateTSInterfaceDeclaration; }, - get createInterfaceBody() { - return TSInterfaceBody.createTSInterfaceBody + get createInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return TSInterfaceBody.createTSInterfaceBody; }, - get updateInterfaceBody() { - return updateTSInterfaceBody + get updateInterfaceBody(): (...args: Parameters) => TSInterfaceBody { + return updateTSInterfaceBody; }, - get createUndefinedLiteral() { - return UndefinedLiteral.createUndefinedLiteral + get createUndefinedLiteral(): ( + ...args: Parameters + ) => UndefinedLiteral { + return UndefinedLiteral.createUndefinedLiteral; }, - get updateUndefinedLiteral() { - return updateUndefinedLiteral + get updateUndefinedLiteral(): (...args: Parameters) => UndefinedLiteral { + return updateUndefinedLiteral; }, - get createAnnotationUsage() { - return AnnotationUsage.createAnnotationUsage + get createAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return AnnotationDeclaration.create1AnnotationDeclaration; }, - get updateAnnotationUsage() { - return updateAnnotationUsage + get updateAnnotationDeclaration(): ( + ...args: Parameters + ) => AnnotationDeclaration { + return updateAnnotationDeclaration; }, - get createAssignmentExpression() { - return AssignmentExpression.create + get createAnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { + return AnnotationUsage.createAnnotationUsage; }, - get updateAssignmentExpression() { - return updateAssignmentExpression + get updateAnnotationUsage(): (...args: Parameters) => AnnotationUsage { + return updateAnnotationUsage; }, - get createETSUndefinedType() { - return ETSUndefinedType.createETSUndefinedType + get create1AnnotationUsage(): ( + ...args: Parameters + ) => AnnotationUsage { + return AnnotationUsage.create1AnnotationUsage; }, - get updateETSUndefinedType() { - return updateETSUndefinedType + get update1AnnotationUsage(): (...args: Parameters) => AnnotationUsage { + return update1AnnotationUsage; }, - get createFunctionSignature() { - return FunctionSignature.createFunctionSignature + get createAssignmentExpression(): ( + ...args: Parameters + ) => AssignmentExpression { + return AssignmentExpression.create; }, - get createConditionalExpression() { - return ConditionalExpression.createConditionalExpression + get updateAssignmentExpression(): (...args: Parameters) => AssignmentExpression { + return updateAssignmentExpression; }, - get updateConditionalExpression() { - return updateConditionalExpression + get createETSUndefinedType(): ( + ...args: Parameters + ) => ETSUndefinedType { + return ETSUndefinedType.createETSUndefinedType; }, - get createTSAsExpression() { - return TSAsExpression.createTSAsExpression + get updateETSUndefinedType(): (...args: Parameters) => ETSUndefinedType { + return updateETSUndefinedType; }, - get updateTSAsExpression() { - return updateTSAsExpression + get createFunctionSignature(): ( + ...args: Parameters + ) => FunctionSignature { + return FunctionSignature.createFunctionSignature; + }, + get createConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return ConditionalExpression.createConditionalExpression; }, - get createThisExpression() { - return ThisExpression.createThisExpression + get updateConditionalExpression(): ( + ...args: Parameters + ) => ConditionalExpression { + return updateConditionalExpression; + }, + get createTSAsExpression(): (...args: Parameters) => TSAsExpression { + return TSAsExpression.createTSAsExpression; + }, + get updateTSAsExpression(): (...args: Parameters) => TSAsExpression { + return updateTSAsExpression; + }, + get createThisExpression(): (...args: Parameters) => ThisExpression { + return ThisExpression.createThisExpression; + }, + get updateThisExpression(): (...args: Parameters) => ThisExpression { + return updateThisExpression; + }, + get createTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return TSTypeAliasDeclaration.createTSTypeAliasDeclaration; + }, + get updateTSTypeAliasDeclaration(): ( + ...args: Parameters + ) => TSTypeAliasDeclaration { + return updateTSTypeAliasDeclaration; + }, + get createTSNonNullExpression(): ( + ...args: Parameters + ) => TSNonNullExpression { + return TSNonNullExpression.createTSNonNullExpression; }, - get updateThisExpression() { - return updateThisExpression + get updateTSNonNullExpression(): (...args: Parameters) => TSNonNullExpression { + return updateTSNonNullExpression; + }, + get createChainExpression(): ( + ...args: Parameters + ) => ChainExpression { + return ChainExpression.createChainExpression; }, - get createTSTypeAliasDeclaration() { - return TSTypeAliasDeclaration.createTSTypeAliasDeclaration + get updateChainExpression(): (...args: Parameters) => ChainExpression { + return updateChainExpression; + }, + get createBlockExpression(): ( + ...args: Parameters + ) => BlockExpression { + return BlockExpression.createBlockExpression; }, - get updateTSTypeAliasDeclaration() { - return updateTSTypeAliasDeclaration + get updateBlockExpression(): (...args: Parameters) => BlockExpression { + return updateBlockExpression; + }, + get createNullLiteral(): (...args: Parameters) => NullLiteral { + return NullLiteral.createNullLiteral; + }, + get updateNullLiteral(): (...args: Parameters) => NullLiteral { + return updateNullLiteral; }, - get createTSNonNullExpression() { - return TSNonNullExpression.createTSNonNullExpression + get createETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression; }, - get updateTSNonNullExpression() { - return updateTSNonNullExpression + get updateETSNewClassInstanceExpression(): ( + ...args: Parameters + ) => ETSNewClassInstanceExpression { + return updateETSNewClassInstanceExpression; + }, + get createETSStringLiteralType(): ( + ...args: Parameters + ) => ETSStringLiteralType { + return ETSStringLiteralType.create; }, - get createChainExpression() { - return ChainExpression.createChainExpression + get createBooleanLiteral(): (...args: Parameters) => BooleanLiteral { + return BooleanLiteral.createBooleanLiteral; }, - get updateChainExpression() { - return updateChainExpression + get createObjectExpression(): ( + ...args: Parameters + ) => ObjectExpression { + return ObjectExpression.createObjectExpression; }, - get createBlockExpression() { - return BlockExpression.createBlockExpression + get updateObjectExpression(): (...args: Parameters) => ObjectExpression { + return updateObjectExpression; }, - get updateBlockExpression() { - return updateBlockExpression + get createProperty(): (...args: Parameters) => Property { + return Property.createProperty; }, - get createNullLiteral() { - return NullLiteral.createNullLiteral + get updateProperty(): (...args: Parameters) => Property { + return updateProperty; }, - get updateNullLiteral() { - return updateNullLiteral + get createTemplateLiteral(): ( + ...args: Parameters + ) => TemplateLiteral { + return TemplateLiteral.createTemplateLiteral; }, - get createETSNewClassInstanceExpression() { - return ETSNewClassInstanceExpression.createETSNewClassInstanceExpression + get updateTemplateLiteral(): (...args: Parameters) => TemplateLiteral { + return updateTemplateLiteral; }, - get updateETSNewClassInstanceExpression() { - return updateETSNewClassInstanceExpression + get createArrayExpression(): ( + ...args: Parameters + ) => ArrayExpression { + return ArrayExpression.createArrayExpression; }, - get createETSStringLiteralType(){ - return ETSStringLiteralType.create; + get updateArrayExpression(): (...args: Parameters) => ArrayExpression { + return updateArrayExpression; + }, + get createTryStatement(): (...args: Parameters) => TryStatement { + return TryStatement.createTryStatement; + }, + 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 { + return ForUpdateStatement.createForUpdateStatement; + }, + get updateForUpdateStatement(): (...args: Parameters) => ForUpdateStatement { + return updateForUpdateStatement; + }, + get createForInStatement(): (...args: Parameters) => ForInStatement { + return ForInStatement.createForInStatement; + }, + get updateForInStatement(): (...args: Parameters) => ForInStatement { + return updateForInStatement; + }, + get createForOfStatement(): (...args: Parameters) => ForOfStatement { + return ForOfStatement.createForOfStatement; + }, + get updateForOfStatement(): (...args: Parameters) => ForOfStatement { + return updateForOfStatement; }, /** @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 b40a1a353ece4ed5753f6a15b2b2a46ff1f0de41..62c02275a505a51ae282d041066c946d32d22dd1 100644 --- a/koala-wrapper/src/arkts-api/factory/nodeTests.ts +++ b/koala-wrapper/src/arkts-api/factory/nodeTests.ts @@ -28,6 +28,8 @@ import { StructDeclaration, VariableDeclaration, VariableDeclarator, + AssignmentExpression, + NumberLiteral } from "../types" import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" @@ -45,7 +47,7 @@ export function isFunctionDeclaration(node: AstNode): node is FunctionDeclaratio } export function isMethodDefinition(node: AstNode): node is MethodDefinition { - return node instanceof MethodDefinition + return global.es2panda._IsMethodDefinition(node.peer); } export function isEtsScript(node: AstNode): node is EtsScript { @@ -87,3 +89,11 @@ export function isIfStatement(node: AstNode): node is IfStatement { export function isVariableDeclarator(node: AstNode): node is VariableDeclarator { return node instanceof VariableDeclarator } + +export function isAssignmentExpression(node: AstNode): node is AssignmentExpression { + return node instanceof AssignmentExpression; +} + +export function isNumberLiteral(node: AstNode): node is NumberLiteral { + return node instanceof NumberLiteral; +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index 463ae2730d764d4bce1b4e50bc50e49697568d51..eef8304a59881a93eb1c6b9e7167d5b73492062e 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -13,65 +13,78 @@ * limitations under the License. */ -export * from "../Es2pandaEnums" -export * from "../generated/Es2pandaEnums" -export * from "../generated/peers/AnnotationUsage" -export * from "../generated/peers/BlockStatement" -export * from "../generated/peers/ETSPrimitiveType" -export * from "../generated/peers/TSInterfaceBody" -export * from "../generated/peers/TSInterfaceDeclaration" -export * from "../generated/peers/TSTypeParameterInstantiation" -export * from "../generated/peers/UndefinedLiteral" -export * from "../generated/peers/ETSUnionType" -export * from "../generated/peers/FunctionSignature" -export * from "../generated/peers/ETSFunctionType" -export * from "../generated/peers/StringLiteral" -export * from "../generated/peers/ThrowStatement" -export * from "../generated/peers/TypeNode" -export * from "../generated/peers/ClassDeclaration" -export * from "../generated/peers/ClassDefinition" -export * from "../generated/peers/Identifier" -export * from "../generated/peers/ETSTypeReference" -export * from "../generated/peers/ETSTypeReferencePart" -export * from "../generated/peers/ClassProperty" -export * from "../generated/peers/ReturnStatement" -export * from "../generated/peers/Expression" -export * from "../generated/peers/Statement" -export * from "../generated/peers/ImportSpecifier" -export * from "../generated/peers/TSAsExpression" -export * from "../generated/peers/ThisExpression" -export * from "../generated/peers/TSThisType" -export * from "../generated/peers/ETSImportDeclaration" -export * from "../generated/peers/ImportSource" -export * from "../generated/peers/ScriptFunction" -export * from "../generated/peers/TSTypeParameterDeclaration" -export * from "../generated/peers/TSTypeParameter" -export * from "../generated/peers/TSNonNullExpression" -export * from "../generated/peers/ChainExpression" -export * from "../generated/peers/ConditionalExpression" -export * from "../generated/peers/NullLiteral" -export * from "../generated/peers/TSTypeAliasDeclaration" -export * from "../generated/peers/ETSUndefinedType" -export * from "../generated/peers/ETSNewClassInstanceExpression" -export * from "../generated/peers/ObjectExpression" -export * from "../generated/peers/Property" -export * from "../generated/peers/BlockExpression" +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'; +export * from '../generated/peers/TSInterfaceBody'; +export * from '../generated/peers/TSInterfaceDeclaration'; +export * from '../generated/peers/TSTypeParameterInstantiation'; +export * from '../generated/peers/UndefinedLiteral'; +export * from '../generated/peers/ETSUnionType'; +export * from '../generated/peers/FunctionSignature'; +export * from '../generated/peers/ETSFunctionType'; +export * from '../generated/peers/StringLiteral'; +export * from '../generated/peers/ThrowStatement'; +export * from '../generated/peers/TypeNode'; +export * from '../generated/peers/ClassDeclaration'; +export * from '../generated/peers/ClassDefinition'; +export * from '../generated/peers/Identifier'; +export * from '../generated/peers/ETSTypeReference'; +export * from '../generated/peers/ETSTypeReferencePart'; +export * from '../generated/peers/ClassProperty'; +export * from '../generated/peers/ReturnStatement'; +export * from '../generated/peers/Expression'; +export * from '../generated/peers/Statement'; +export * from '../generated/peers/ImportSpecifier'; +export * from '../generated/peers/TSAsExpression'; +export * from '../generated/peers/ThisExpression'; +export * from '../generated/peers/TSThisType'; +export * from '../generated/peers/ETSImportDeclaration'; +export * from '../generated/peers/ImportSource'; +export * from '../generated/peers/ScriptFunction'; +export * from '../generated/peers/TSTypeParameterDeclaration'; +export * from '../generated/peers/TSTypeParameter'; +export * from '../generated/peers/TSNonNullExpression'; +export * from '../generated/peers/ChainExpression'; +export * from '../generated/peers/ConditionalExpression'; +export * from '../generated/peers/NullLiteral'; +export * from '../generated/peers/TSTypeAliasDeclaration'; +export * from '../generated/peers/ETSUndefinedType'; +export * from '../generated/peers/ETSNewClassInstanceExpression'; +export * from '../generated/peers/ObjectExpression'; +export * from '../generated/peers/Property'; +export * from '../generated/peers/BlockExpression'; +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 "./types" -export * from "./utilities/private" -export * from "./utilities/public" -export * from "./utilities/performance" -export * from "./factory/nodeFactory" -export * from "./factory/nodeTests" -export * from "./visitor" -export * from "./peers/AstNode" -import "../generated" +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'; +export * from './peers/AstNode'; +import '../generated'; -export * from "./peers/Config" -export * from "./peers/Context" -export * from "./peers/Program" -export * from "./peers/ImportPathManager" -export * from "./peers/SourcePosition" -export * from "./to-be-generated/MemberExpression" -export * from "./static/globalUtils" -export { global as arktsGlobal } from "./static/global"; +export * from './peers/Config'; +export * from './peers/Context'; +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/AnnotationUsage.ts b/koala-wrapper/src/arkts-api/node-utilities/AnnotationUsage.ts index e79cc04c345516a77fae5946f3ca5eeb71a0556f..82b28fec3036a8fb56826c7bcc29fb462ef82405 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/AnnotationUsage.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/AnnotationUsage.ts @@ -16,6 +16,7 @@ import { AnnotationUsage, Expression } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { AstNode } from '../peers/AstNode'; export function updateAnnotationUsage(original: AnnotationUsage, expr?: Expression): AnnotationUsage { if (isSameNativeObject(expr, original.expr)) { @@ -25,3 +26,12 @@ export function updateAnnotationUsage(original: AnnotationUsage, expr?: Expressi const update = updateThenAttach(AnnotationUsage.updateAnnotationUsage, attachModifiers); return update(original, expr); } + +export function update1AnnotationUsage(original: AnnotationUsage, expr: Expression | undefined, properties: readonly AstNode[]): AnnotationUsage { + if (isSameNativeObject(expr, original.expr) && isSameNativeObject(properties, original.properties)) { + return original; + } + + const update = updateThenAttach(AnnotationUsage.update1AnnotationUsage, attachModifiers); + return update(original, expr, properties); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ArrayExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ArrayExpression.ts new file mode 100644 index 0000000000000000000000000000000000000000..320b0d678bd9f868eddc5eeeaffb2aa1ef1c195d --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ArrayExpression.ts @@ -0,0 +1,38 @@ +/* + * 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 { ArrayExpression, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateArrayExpression(original: ArrayExpression, elements: readonly Expression[]): ArrayExpression { + if (isSameNativeObject(elements, original.elements)) { + return original; + } + const update = updateThenAttach( + ArrayExpression.updateArrayExpression, + attachModifiers, + attachOptionalAndDeclaration + ); + return update(original, elements); +} + +function attachOptionalAndDeclaration(node: ArrayExpression, original: ArrayExpression): ArrayExpression { + if (node.isDeclaration) { + node.setDeclaration(); + } + node.setOptional(original.isOptional); + return node; +} 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/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/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/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/ForInStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ForInStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..40f78ab063b300786aaddf46ff46f6d4abb8d7fc --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ForInStatement.ts @@ -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 { ForInStatement, Statement, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { AstNode } from '../peers/AstNode'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateForInStatement( + original: ForInStatement, + left?: AstNode, + right?: Expression, + body?: Statement +): ForInStatement { + if ( + isSameNativeObject(left, original.left) && + isSameNativeObject(right, original.right) && + isSameNativeObject(body, original.body) + ) { + return original; + } + + const updateNode = updateThenAttach(ForInStatement.updateForInStatement, attachModifiers); + return updateNode(original, left, right, body); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ForOfStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ForOfStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c8d99a1d9f350572c5752a21b4e6dbdc5650908 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ForOfStatement.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 { ForOfStatement, Statement, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { AstNode } from '../peers/AstNode'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateForOfStatement( + original: ForOfStatement, + left: AstNode | undefined, + right: Expression | undefined, + body: Statement | undefined, + isAwait: boolean +): ForOfStatement { + if ( + isSameNativeObject(left, original.left) && + isSameNativeObject(right, original.right) && + isSameNativeObject(body, original.body) + ) { + return original; + } + + const updateNode = updateThenAttach(ForOfStatement.updateForOfStatement, attachModifiers); + return updateNode(original, left, right, body, isAwait); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/ForUpdateStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ForUpdateStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..0fa41007b56654e89af71ec85e5973361f2e4057 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ForUpdateStatement.ts @@ -0,0 +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 { ForUpdateStatement, Statement, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { AstNode } from '../peers/AstNode'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateForUpdateStatement( + original: ForUpdateStatement, + init?: AstNode, + test?: Expression, + update?: Expression, + body?: Statement +): ForUpdateStatement { + if ( + isSameNativeObject(init, original.init) && + isSameNativeObject(test, original.test) && + isSameNativeObject(update, original.update) && + isSameNativeObject(body, original.body) + ) { + return original; + } + + const updateNode = updateThenAttach(ForUpdateStatement.updateForUpdateStatement, attachModifiers); + return updateNode(original, init, test, update, body); +} 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 44afa1dbc81256c5a4859b32ce7d08e682a74bae..6318a0c84fd2c28d360abd7169b37f8eaec4c74b 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts @@ -19,12 +19,14 @@ import { AstNode } from '../peers/AstNode'; 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, kind: Es2pandaMethodDefinitionKind, key: AstNode, - value: AstNode, + value: ScriptFunction, modifiers: KInt, isComputed: boolean ): MethodDefinition { @@ -41,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/ObjectExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ObjectExpression.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac7a9d6b6931757ebb551b30a9c01ff43062cec5 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/ObjectExpression.ts @@ -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 { ObjectExpression, Property } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; + +export function updateObjectExpression( + original: ObjectExpression, + nodeType: Es2pandaAstNodeType, + properties: Property[], + trailingComma: boolean +): ObjectExpression { + if ( + isSameNativeObject(properties, original.properties) + /* TODO: no getter for nodeType */ + /* TODO: no getter for trailingComma */ + ) { + return original; + } + + const update = updateThenAttach(ObjectExpression.updateObjectExpression, attachModifiers); + return update(original, nodeType, properties, trailingComma); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/Property.ts b/koala-wrapper/src/arkts-api/node-utilities/Property.ts new file mode 100644 index 0000000000000000000000000000000000000000..fdb1443c593831eb1158f28398b4ed6484af27e5 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/Property.ts @@ -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 { 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 { + if (isSameNativeObject(key, original.key) && isSameNativeObject(value, original.value)) { + return original; + } + + const update = updateThenAttach(Property.updateProperty, attachModifiers); + 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/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/TemplateLiteral.ts b/koala-wrapper/src/arkts-api/node-utilities/TemplateLiteral.ts new file mode 100644 index 0000000000000000000000000000000000000000..840c0d12040a13f3a2c4e3704f4e0ca3fb7d68aa --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/TemplateLiteral.ts @@ -0,0 +1,35 @@ +/* + * 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 { TemplateLiteral, TemplateElement, Expression } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateTemplateLiteral( + original: TemplateLiteral, + quasis: readonly TemplateElement[], + expressions: readonly Expression[], + multilineString: string +): TemplateLiteral { + if ( + isSameNativeObject(quasis, original.quasis) && + isSameNativeObject(expressions, original.expressions) + ) { + return original; + } + + const update = updateThenAttach(TemplateLiteral.updateTemplateLiteral, attachModifiers); + return update(original, quasis, expressions, multilineString); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/TryStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/TryStatement.ts new file mode 100644 index 0000000000000000000000000000000000000000..32512066cbba85065a851c78200a77277bd0cd75 --- /dev/null +++ b/koala-wrapper/src/arkts-api/node-utilities/TryStatement.ts @@ -0,0 +1,41 @@ +/* + * 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 { LabelPair } from '../../generated/peers/LabelPair'; +import { BlockStatement, CatchClause, Statement, TryStatement } from '../../generated'; +import { isSameNativeObject } from '../peers/ArktsObject'; +import { attachModifiers, updateThenAttach } from '../utilities/private'; + +export function updateTryStatement( + original: TryStatement, + block: BlockStatement | undefined, + catchClauses: readonly CatchClause[], + finalizer: BlockStatement | undefined, + finalizerInsertionsLabelPair: readonly LabelPair[], + finalizerInsertionsStatement: readonly Statement[] +): TryStatement { + if ( + isSameNativeObject(block, original.block) && + isSameNativeObject(catchClauses, original.catchClauses) && + isSameNativeObject(finalizer, original.finallyBlock) + /* TODO: no getter for finalizerInsertionsLabelPair */ + /* TODO: no getter for finalizerInsertionsStatement */ + ) { + return original; + } + + const update = updateThenAttach(TryStatement.updateTryStatement, attachModifiers); + return update(original, block, catchClauses, finalizer, finalizerInsertionsLabelPair, finalizerInsertionsStatement); +} diff --git a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclaration.ts index 24432ec6da992e049bf2c470807b40b6cd05c2d6..0bd18e52d7ff919ae0ae03bf7f8a5dc27df7ce18 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclaration.ts @@ -33,6 +33,10 @@ export function updateVariableDeclaration( return original; } - const update = updateThenAttach(VariableDeclaration.update, attachModifiers); + const update = updateThenAttach( + VariableDeclaration.update, + attachModifiers, + (node: VariableDeclaration, original: VariableDeclaration) => node.setAnnotations(original.annotations) + ); return update(original, modifiers, kind, declarators); } 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 634da8e17b7fd3cc001df79a4588e4b608fa23a9..2a8b90fa6911fb0c75dd3f1c8dd3365a0a48df66 100644 --- a/koala-wrapper/src/arkts-api/peers/AstNode.ts +++ b/koala-wrapper/src/arkts-api/peers/AstNode.ts @@ -13,117 +13,150 @@ * 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 { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +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 { - return 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; + } + 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 81a1ec31fc92a7bbd78fc4ab6f928633c11b43a5..d091e7fd37fa67f936fc99ed255872dea0fb17e2 100644 --- a/koala-wrapper/src/arkts-api/peers/Context.ts +++ b/koala-wrapper/src/arkts-api/peers/Context.ts @@ -13,39 +13,54 @@ * limitations under the License. */ -import { ArktsObject } from "./ArktsObject" -import { global } from "../static/global" -import { throwError, filterSource } from "../../utils" -import { passString } from "../utilities/private" -import { KNativePointer } from "@koalaui/interop" -import { AstNode } from "./AstNode" -import { Program } from "./Program" +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { throwError, filterSource } from '../../utils'; +import { passString, passStringArray } from '../utilities/private'; +import { KBoolean, KInt, KNativePointer } from '@koalaui/interop'; +import { AstNode } from './AstNode'; +import { Program } from './Program'; export class Context extends ArktsObject { constructor(peer: KNativePointer) { - super(peer) + super(peer); } - static createFromString( - source: string - ): Context { + static createFromString(source: string): Context { if (!global.configIsInitialized()) { - throwError(`Config not initialized`) + throwError(`Config not initialized`); } return new Context( - global.es2panda._CreateContextFromString( - global.config, - passString(source), - passString(global.filePath) - ) - ) + global.es2panda._CreateContextFromString(global.config, passString(source), passString(global.filePath)) + ); } - static destroyAndRecreate( - ast: AstNode + + static createCacheContextFromFile( + configPtr: KNativePointer, + fileName: string, + globalContextPtr: KNativePointer, + isExternal: KBoolean ): 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.es2panda._CreateCacheContextFromFile(configPtr, passString(fileName), globalContextPtr, isExternal) + ); + } + + static createContextGenerateAbcForExternalSourceFiles( + filenames: string[]): Context { + if (!global.configIsInitialized()) { + throwError(`Config not initialized`); + } + return new Context( + global.es2panda._CreateContextGenerateAbcForExternalSourceFiles(global.config, filenames.length, passStringArray(filenames)) + ); + } + + + 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); } @@ -53,4 +68,4 @@ export class Context extends ArktsObject { get program(): Program { return new Program(global.es2panda._ContextProgram(this.peer)); } -} \ No newline at end of file +} 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..79f24ec0bf1a01a3275c4345956b9695d8ddce5c --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/Diagnostic.ts @@ -0,0 +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 { 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, + range: SourceRange): void { + global.es2panda._LogDiagnosticWithSuggestion(global.context, diagnosticInfo.peer, suggestionInfo.peer, range.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..d67f7e839a14ae829b903d328300179728772f6b --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/DiagnosticInfo.ts @@ -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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { passStringArray } from '../utilities/private'; + +export class DiagnosticInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, ...args: string[]): DiagnosticInfo { + return new DiagnosticInfo( + global.es2panda._CreateDiagnosticInfo(global.context, kind.peer, passStringArray(args), args.length) + ); + } + +} \ 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/Program.ts b/koala-wrapper/src/arkts-api/peers/Program.ts index 5f3afbed2d791a58cd359de519e2a3d8412cff2e..5733592bc642d34d895e9c38f494e0b7d1dcb13e 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,54 @@ 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)); + } + + 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/SourceRange.ts b/koala-wrapper/src/arkts-api/peers/SourceRange.ts new file mode 100644 index 0000000000000000000000000000000000000000..5ecee4ccbd4f2d9b694edbd9968daff4a7d7d4ab --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/SourceRange.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 { SourcePosition } from './SourcePosition'; +import { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; + +export class SourceRange extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(start: SourcePosition, end: SourcePosition): SourceRange { + return new SourceRange( + global.es2panda._CreateSourceRange(global.context, start.peer, end.peer) + ); + } + + start(): SourcePosition { + return new SourcePosition(global.es2panda._SourceRangeStart(global.context, this.peer)); + } + + end(): SourcePosition { + return new SourcePosition(global.es2panda._SourceRangeEnd(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..cb43685c988fc48965db39e65557d94b622c9a1a --- /dev/null +++ b/koala-wrapper/src/arkts-api/peers/SuggestionInfo.ts @@ -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 { ArktsObject } from './ArktsObject'; +import { global } from '../static/global'; +import { KNativePointer } from '@koalaui/interop'; +import { DiagnosticKind } from './DiagnosticKind'; +import { passStringArray } from '../utilities/private'; + +export class SuggestionInfo extends ArktsObject { + constructor(peer: KNativePointer) { + super(peer); + } + + static create(kind: DiagnosticKind, substitutionCode: string, ...args: string[]): SuggestionInfo { + return new SuggestionInfo( + global.es2panda._CreateSuggestionInfo(global.context, kind.peer, passStringArray(args), args.length, substitutionCode) + ); + } + +} \ No newline at end of file diff --git a/koala-wrapper/src/arkts-api/types.ts b/koala-wrapper/src/arkts-api/types.ts index 3faf2f690345287449c3b3767846aba28a2a6ac1..d73f539d0750207a7aacaa766b37e2d78a15a0fa 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, @@ -48,6 +47,7 @@ import { nodeByType } from './class-by-peer'; import { MemberExpression } from './to-be-generated/MemberExpression'; import { AnnotationUsage, + ArrayExpression, BlockStatement, ClassDefinition, ETSTypeReference, @@ -89,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)) ); @@ -240,6 +240,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[]; @@ -665,9 +673,8 @@ export class MethodDefinition extends AstNode { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION); super(peer); this.kind = global.generatedEs2panda._MethodDefinitionKindConst(global.context, this.peer); - this.scriptFunction = unpackNonNullableNode( - global.generatedEs2panda._MethodDefinitionFunction(global.context, this.peer) - ); + this.funcExpr = unpackNonNullableNode(global.generatedEs2panda._ClassElementValue(global.context, this.peer)); + this.scriptFunction = this.funcExpr.scriptFunction; assertValidPeer(this.scriptFunction.peer, Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION); // Somehow the scriptFunction cannot attach method's key to its ident after checker @@ -686,7 +693,7 @@ export class MethodDefinition extends AstNode { static create( kind: Es2pandaMethodDefinitionKind, key: AstNode, - value: AstNode, + value: ScriptFunction, modifiers: KInt, isComputed: boolean ): MethodDefinition { @@ -695,7 +702,7 @@ export class MethodDefinition extends AstNode { global.context, kind, passNode(key), - passNode(value), + passNode(FunctionExpression.create(value)), modifiers, isComputed ), @@ -707,7 +714,7 @@ export class MethodDefinition extends AstNode { node: MethodDefinition, kind: Es2pandaMethodDefinitionKind, key: AstNode, - value: AstNode, + value: ScriptFunction, modifiers: KInt, isComputed: boolean ): MethodDefinition { @@ -717,7 +724,7 @@ export class MethodDefinition extends AstNode { node.peer, kind, passNode(key), - passNode(value), + passNode(FunctionExpression.update(node.funcExpr, value)), modifiers, isComputed ), @@ -747,6 +754,7 @@ export class MethodDefinition extends AstNode { readonly kind: Es2pandaMethodDefinitionKind; readonly scriptFunction: ScriptFunction; readonly name: Identifier; + readonly funcExpr: FunctionExpression; } export class VariableDeclaration extends AstNode { @@ -793,6 +801,22 @@ export class VariableDeclaration extends AstNode { return new VariableDeclaration(peer); } + get annotations(): readonly AnnotationUsage[] { + return unpackNodeArray( + global.generatedEs2panda._VariableDeclarationAnnotationsConst(global.context, this.peer) + ); + } + /** @deprecated */ + setAnnotations(annotations: readonly AnnotationUsage[]): this { + global.generatedEs2panda._VariableDeclarationSetAnnotations( + global.context, + this.peer, + passNodeArray(annotations), + annotations.length + ); + return this; + } + readonly declarationKind: Es2pandaVariableDeclarationKind; readonly declarators: readonly VariableDeclarator[]; } @@ -899,5 +923,6 @@ const pairs: [Es2pandaAstNodeType, { new (peer: KNativePointer): AstNode }][] = [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE, ETSTypeReference], [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART, ETSTypeReferencePart], [Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, ObjectExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ARRAY_EXPRESSION, ArrayExpression], ]; pairs.forEach(([nodeType, astNode]) => nodeByType.set(nodeType, astNode)); 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..0843ee6b6b909644b51b3f852e8cda3a0ac0ec31 --- /dev/null +++ b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + 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 45888cd18273f57abb6582aafbd0e74e4351b97e..c630d2ac85866cde6f5bebbba1688e648505c204 100644 --- a/koala-wrapper/src/arkts-api/utilities/public.ts +++ b/koala-wrapper/src/arkts-api/utilities/public.ts @@ -13,88 +13,179 @@ * limitations under the License. */ -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 { isFunctionDeclaration, isMemberExpression } from "../factory/nodeTests" -import { Es2pandaContextState, Es2pandaModifierFlags } from "../../generated/Es2pandaEnums" -import type { AstNode } from "../peers/AstNode" -import { ClassDefinition, ClassProperty, ETSImportDeclaration, isClassDefinition, isScriptFunction, type AnnotationUsage } from "../../generated" -import { Program } from "../peers/Program" -import { clearNodeCache } from "../class-by-peer" - -export function proceedToState(state: Es2pandaContextState, forceDtsEmit = false): void { - console.log("[TS WRAPPER] PROCEED TO STATE: ", getEnumName(Es2pandaContextState, state)); - if (state <= global.es2panda._ContextState(global.context)) { - console.log("[TS WRAPPER] PROCEED TO STATE: SKIPPING"); - return - } - clearNodeCache() +import { global } from '../static/global'; +import { isNumber, throwError, getEnumName } from '../../utils'; +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, + Es2pandaMethodDefinitionKind, + Es2pandaModifierFlags, +} from '../../generated/Es2pandaEnums'; +import type { AstNode } from '../peers/AstNode'; +import { + ClassDefinition, + ClassProperty, + ETSImportDeclaration, + ImportSpecifier, + isClassDefinition, + isIdentifier, + isObjectExpression, + isProperty, + isScriptFunction, + isTSInterfaceDeclaration, + Property, + type AnnotationUsage, +} from '../../generated'; +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, context: KNativePointer, forceDtsEmit = false): void { + console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); + if (global.es2panda._ContextState(context) === Es2pandaContextState.ES2PANDA_STATE_ERROR) { + clearNodeCache(); + processErrorState(state, context, forceDtsEmit); + } + if (state <= global.es2panda._ContextState(context)) { + console.log('[TS WRAPPER] PROCEED TO STATE: SKIPPING'); + return; + } + clearNodeCache(); + global.es2panda._ProceedToState(context, state); + processErrorState(state, context, forceDtsEmit); +} + +function processErrorState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { try { - global.es2panda._ProceedToState(global.context, state) - 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`) + throwError(`Could not get ContextErrorMessage`); + } + const allErrorMessages = withStringResult(global.es2panda._GetAllErrorMessages(context)); + if (allErrorMessages === undefined) { + throwError(`Could not get AllErrorMessages`); } - throwError( - [ - `Failed to proceed to ${Es2pandaContextState[state]}`, - errorMessage - ] - .join(`\n`) - ) + throwError([`Failed to proceed to ${Es2pandaContextState[state]}`, errorMessage, allErrorMessages].join(`\n`)); } } catch (e) { - global.es2panda._DestroyContext(global.context) - throw e + 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) + return global.es2panda._CheckerStartChecker(global.context); } export function recheckSubtree(node: AstNode): void { - global.es2panda._AstNodeRecheck(global.context, node.peer) + global.es2panda._AstNodeRecheck(global.context, node.peer); } export function rebindSubtree(node: AstNode): void { - global.es2panda._AstNodeRebind(global.context, node.peer) + global.es2panda._AstNodeRebind(global.context, node.peer); } export function getDecl(node: AstNode): AstNode | undefined { if (isMemberExpression(node)) { - return getDecl(node.property) + return getDeclFromArrayOrObjectMember(node); + } + if (isObjectExpression(node)) { + return getPeerObjectDecl(passNode(node)); + } + const decl = getPeerDecl(passNode(node)); + if (!!decl) { + return decl; + } + if (!!node.parent && isProperty(node.parent)) { + return getDeclFromProperty(node.parent); + } + return undefined; +} + +function getDeclFromProperty(node: Property): AstNode | undefined { + if (!node.key) { + return undefined; } - const decl = global.es2panda._DeclarationFromIdentifier(global.context, passNode(node)) + if (!!node.parent && !isObjectExpression(node.parent)) { + return getPeerDecl(passNode(node.key)); + } + return getDeclFromObjectExpressionProperty(node); +} + +function getDeclFromObjectExpressionProperty(node: Property): AstNode | undefined { + const declNode = getPeerObjectDecl(passNode(node.parent)); + if (!declNode || !node.key || !isIdentifier(node.key)) { + return undefined; + } + let body: readonly AstNode[] = []; + if (isClassDefinition(declNode)) { + body = declNode.body; + } else if (isTSInterfaceDeclaration(declNode)) { + body = declNode.body?.body ?? []; + } + return body.find( + (statement) => + isMethodDefinition(statement) && + statement.kind === Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET && + !!statement.name && + !!node.key && + isIdentifier(node.key) && + statement.name.name === node.key.name + ); +} + +function getDeclFromArrayOrObjectMember(node: MemberExpression): AstNode | undefined { + if (isNumberLiteral(node.property)) { + return getDecl(node.object); + } + return getDecl(node.property); +} + +export function getPeerDecl(peer: KNativePointer): AstNode | undefined { + const decl = global.es2panda._DeclarationFromIdentifier(global.context, peer); if (decl === nullptr) { - return undefined + return undefined; } - return unpackNonNullableNode(decl) + return unpackNonNullableNode(decl); +} + +export function getPeerObjectDecl(peer: KNativePointer): AstNode | undefined { + const decl = global.es2panda._ClassVariableDeclaration(global.context, peer); + if (decl === nullptr) { + return undefined; + } + return unpackNonNullableNode(decl); } export function getAnnotations(node: AstNode): readonly AnnotationUsage[] { if (!isFunctionDeclaration(node) && !isScriptFunction(node) && !isClassDefinition(node)) { - throwError('for now annotations allowed only for: functionDeclaration, scriptFunction, classDefinition') + throwError('for now annotations allowed only for: functionDeclaration, scriptFunction, classDefinition'); } - return unpackNodeArray(global.es2panda._AnnotationAllowedAnnotations(global.context, node.peer, nullptr)) + return unpackNodeArray(global.es2panda._AnnotationAllowedAnnotations(global.context, node.peer, nullptr)); } export function getOriginalNode(node: AstNode): AstNode { if (node === undefined) { // TODO: fix this - throwError('there is no arkts pair of ts node (unable to getOriginalNode)') + throwError('there is no arkts pair of ts node (unable to getOriginalNode)'); } if (node.originalPeer === nullptr) { - return node + return node; } - return unpackNonNullableNode(node.originalPeer) + return unpackNonNullableNode(node.originalPeer); } export function getFileName(): string { - return global.filePath + return global.filePath; } export function classDefinitionSetFromStructModifier(node: ClassDefinition): void { @@ -105,17 +196,29 @@ export function classDefinitionIsFromStructConst(node: ClassDefinition): boolean return global.es2panda._ClassDefinitionIsFromStructConst(global.context, node.peer); } +export function ImportSpecifierSetRemovable(node: ImportSpecifier): void { + global.es2panda._ImportSpecifierSetRemovable(global.context, node.peer); +} + +export function ImportSpecifierIsRemovableConst(node: ImportSpecifier): boolean { + return global.es2panda._ImportSpecifierIsRemovableConst(global.context, node.peer); +} + // TODO: It seems like Definition overrides AstNode modifiers // with it's own modifiers which is completely unrelated set of flags. // Use this function if you need // the language level modifiers: public, declare, export, etc. export function classDefinitionFlags(node: ClassDefinition): Es2pandaModifierFlags { - return global.generatedEs2panda._AstNodeModifiers(global.context, node.peer) + return global.generatedEs2panda._AstNodeModifiers(global.context, node.peer); } // TODO: Import statements should be inserted to the statements export function importDeclarationInsert(node: ETSImportDeclaration, program: Program): void { - global.es2panda._InsertETSImportDeclarationAndParse(global.context, program.peer, node.peer) + 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 { @@ -125,7 +228,7 @@ export function hasModifierFlag(node: AstNode, flag: Es2pandaModifierFlags): boo if (isClassDefinition(node)) { modifiers = classDefinitionFlags(node); } else { - modifiers = node.modifiers + modifiers = node.modifiers; } return (modifiers & flag) === flag; } @@ -143,20 +246,81 @@ export function classPropertySetOptional(node: ClassProperty, value: boolean): C export function modifiersToString(modifiers: Es2pandaModifierFlags): string { return Object.values(Es2pandaModifierFlags) .filter(isNumber) - .map(it => { - console.log(it.valueOf(), Es2pandaModifierFlags[it], modifiers.valueOf() & it) - return ((modifiers.valueOf() & it) === it) ? Es2pandaModifierFlags[it] : "" - }).join(" ") + .map((it) => { + console.log(it.valueOf(), Es2pandaModifierFlags[it], modifiers.valueOf() & it); + return (modifiers.valueOf() & it) === it ? Es2pandaModifierFlags[it] : ''; + }) + .join(' '); } export function destroyConfig(config: KNativePointer): void { global.es2panda._DestroyConfig(config); global.resetConfig(); } -export function setAllParents(ast: AstNode) { - global.es2panda._AstNodeUpdateAll(global.context, ast.peer) +export function setAllParents(ast: AstNode): void { + global.es2panda._AstNodeUpdateAll(global.context, ast.peer); +} + +export function generateTsDeclarationsFromContext( + outputDeclEts: string, + outputEts: string, + exportAll: boolean, + isolated: boolean +): KInt { + return global.es2panda._GenerateTsDeclarationsFromContext( + global.context, + passString(outputDeclEts), + passString(outputEts), + exportAll, + isolated + ); +} + +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); } -export function generateTsDeclarationsFromContext(outputDeclEts: string, outputEts: string, exportAll: boolean): KInt { - return global.es2panda._GenerateTsDeclarationsFromContext(global.context, passString(outputDeclEts), passString(outputEts), exportAll) -} \ No newline at end of file +export function getStartPosition(node: AstNode): SourcePosition { + return new SourcePosition(global.es2panda._AstNodeStartConst(global.context, node.peer)); +} + +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); +} diff --git a/koala-wrapper/src/arkts-api/visitor.ts b/koala-wrapper/src/arkts-api/visitor.ts index 2d62b5a428840c858d57653b2d66dbd33cd679a7..3a76bf8404a5ecb303cac0db7b933874c9f8d9c0 100644 --- a/koala-wrapper/src/arkts-api/visitor.ts +++ b/koala-wrapper/src/arkts-api/visitor.ts @@ -13,21 +13,21 @@ * limitations under the License. */ -import { global } from "./static/global" -import { factory } from "./factory/nodeFactory" +import { global } from './static/global'; +import { factory } from './factory/nodeFactory'; import { Es2pandaClassDefinitionModifiers, Es2pandaImportKinds, Es2pandaModifierFlags, - Es2pandaVariableDeclaratorFlag -} from "../generated/Es2pandaEnums" -import { AstNode } from "./peers/AstNode" -import { - isBlockStatement, - isConditionalExpression, - isTSInterfaceBody, - isTSInterfaceDeclaration, - isClassDeclaration, + Es2pandaVariableDeclaratorFlag, +} from '../generated/Es2pandaEnums'; +import { AstNode } from './peers/AstNode'; +import { + isBlockStatement, + isConditionalExpression, + isTSInterfaceBody, + isTSInterfaceDeclaration, + isClassDeclaration, isClassDefinition, isTSAsExpression, isETSImportDeclaration, @@ -36,8 +36,25 @@ import { FunctionSignature, Property, isClassProperty, - isImportDeclaration -} from "../generated" + isImportDeclaration, + isObjectExpression, + ObjectExpression, + isProperty, + Expression, + isETSNewClassInstanceExpression, + isTemplateLiteral, + isBlockExpression, + isReturnStatement, + isArrayExpression, + isTryStatement, + isBinaryExpression, + isForInStatement, + isForUpdateStatement, + isForOfStatement, + isTSTypeAliasDeclaration, + isETSParameterExpression, + isETSFunctionType, +} from '../generated'; import { isEtsScript, isCallExpression, @@ -50,112 +67,61 @@ import { isIfStatement, isVariableDeclaration, isVariableDeclarator, - isArrowFunctionExpression -} from "./factory/nodeTests" -import { - classDefinitionFlags, - hasModifierFlag, - classPropertySetOptional - } from "./utilities/public" - -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); - } + isArrowFunctionExpression, + isAssignmentExpression, + isEtsParameterExpression, +} from './factory/nodeTests'; +import { classDefinitionFlags } from './utilities/public'; +import { Es2pandaAstNodeType } from '../Es2pandaEnums'; - 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(); - } -} +type Visitor = (node: AstNode) => AstNode; // TODO: rethink (remove as) function nodeVisitor(node: T, visitor: Visitor): T { if (node === undefined) { - return node + return node; } - return visitor(node) as T + return visitor(node) as T; } // TODO: rethink (remove as) -function nodesVisitor(nodes: TIn, visitor: Visitor): T[] | TIn { +function nodesVisitor( + nodes: TIn, + visitor: Visitor +): T[] | TIn { if (nodes === undefined) { - return nodes + return nodes; } - return nodes.map(node => visitor(node) as T) + return nodes.map((node) => visitor(node) as T); } -// TODO: apply this to all nodes that does not require updating -function visitWithoutUpdate( - node: T, - visitor: Visitor -): T { - if (isImportDeclaration(node)) { - nodesVisitor(node.specifiers, visitor); - } - return node; +let updated: boolean = false; + +export function visitEachChild(node: AstNode, visitor: Visitor): AstNode { + updated = false; + let script: AstNode = node; + script = visitETSModule(script, visitor); + script = visitDeclaration(script, visitor); + script = visitDefinition(script, visitor); + script = visitDefinitionBody(script, visitor); + script = visitStatement(script, visitor); + script = visitForLoopStatement(script, visitor); + script = visitOuterExpression(script, visitor); + script = visitInnerExpression(script, visitor); + script = visitTrivialExpression(script, visitor); + script = visitLiteral(script, visitor); + // TODO + return visitWithoutUpdate(script, visitor); } -export function visitEachChild( - node: AstNode, - visitor: Visitor -): AstNode { - if (isEtsScript(node)) { - return factory.updateEtsScript( - node, - nodesVisitor(node.statements, visitor) - ); - } - if (isCallExpression(node)) { +function visitOuterExpression(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } else if (isBlockExpression(node)) { + updated = true; + return factory.updateBlockExpression(node, nodesVisitor(node.statements, visitor)); + } else if (isCallExpression(node)) { + updated = true; const call = factory.updateCallExpression( node, nodeVisitor(node.expression, visitor), @@ -166,41 +132,155 @@ export function visitEachChild( call.setTralingBlock(nodeVisitor(node.trailingBlock, visitor)); } return call; + } else if (isArrowFunctionExpression(node)) { + updated = true; + return factory.updateArrowFunction(node, nodeVisitor(node.scriptFunction, visitor)); + } else if (isAssignmentExpression(node)) { + updated = true; + return factory.updateAssignmentExpression( + node, + nodeVisitor(node.left as Expression, visitor), + node.operatorType, + nodeVisitor(node.right as Expression, visitor) + ); + } else if (isETSNewClassInstanceExpression(node)) { + updated = true; + return factory.updateETSNewClassInstanceExpression( + node, + node.getTypeRef, + nodesVisitor(node.getArguments, visitor) + ); } - if (isFunctionDeclaration(node)) { - return factory.updateFunctionDeclaration( + if (isArrayExpression(node)) { + updated = true; + return factory.updateArrayExpression(node, nodesVisitor(node.elements, visitor)); + } + return node; +} + +function visitInnerExpression(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isMemberExpression(node)) { + updated = true; + return factory.updateMemberExpression( node, - nodeVisitor(node.scriptFunction, visitor), - node.isAnon, - node.annotations, + nodeVisitor(node.object, visitor), + nodeVisitor(node.property, visitor), + node.kind, + node.computed, + node.optional ); } - if (isBlockStatement(node)) { - return factory.updateBlock( + if (isConditionalExpression(node)) { + updated = true; + return factory.updateConditionalExpression( node, - nodesVisitor(node.statements, visitor), + nodeVisitor(node.test, visitor), + nodeVisitor(node.consequent, visitor), + nodeVisitor(node.alternate, visitor) ); } - if (isExpressionStatement(node)) { - return factory.updateExpressionStatement( + if (isTSAsExpression(node)) { + updated = true; + return factory.updateTSAsExpression( node, - nodeVisitor(node.expression, visitor) + nodeVisitor(node.expr, visitor), + nodeVisitor(node.typeAnnotation, visitor), + node.isConst ); } - if (isClassDeclaration(node)) { - return factory.updateClassDeclaration( + if (isObjectExpression(node)) { + updated = true; + return factory.updateObjectExpression( node, - nodeVisitor(node.definition, visitor) + Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + nodesVisitor(node.properties as Property[], visitor), + false ); } - if (isStructDeclaration(node)) { - return factory.updateStructDeclaration( + if (isProperty(node)) { + updated = true; + return factory.updateProperty(node, node.key, nodeVisitor(node.value, visitor)); + } + // TODO + return node; +} + +function visitTrivialExpression(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isBinaryExpression(node)) { + updated = true; + return factory.updateBinaryExpression( node, - nodeVisitor(node.definition, visitor) + nodeVisitor(node.left, visitor), + nodeVisitor(node.right, visitor), + node.operatorType ); } + // TODO + return node; +} + +function visitDeclaration(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } else if (isFunctionDeclaration(node)) { + updated = true; + return factory.updateFunctionDeclaration( + node, + nodeVisitor(node.scriptFunction, visitor), + node.isAnon, + node.annotations + ); + } else if (isClassDeclaration(node)) { + updated = true; + return factory.updateClassDeclaration(node, nodeVisitor(node.definition, visitor)); + } else if (isStructDeclaration(node)) { + updated = true; + return factory.updateStructDeclaration(node, nodeVisitor(node.definition, visitor)); + } else if (isTSInterfaceDeclaration(node)) { + updated = true; + return factory.updateInterfaceDeclaration( + node, + nodesVisitor(node.extends, visitor), + nodeVisitor(node.id, visitor), + nodeVisitor(node.typeParams, visitor), + nodeVisitor(node.body, visitor), + node.isStatic, + // TODO: how do I get it? + true + ); + } else if (isVariableDeclaration(node)) { + updated = true; + return factory.updateVariableDeclaration( + node, + 0, + node.declarationKind, + nodesVisitor(node.declarators, visitor) + ); + } else if (isTSTypeAliasDeclaration(node)) { + updated = true; + return factory.updateTSTypeAliasDeclaration( + node, + node.id, + nodeVisitor(node.typeParams, visitor), + nodeVisitor(node.typeAnnotation, visitor) + ); + } + // TODO + return node; +} + +function visitDefinition(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } if (isClassDefinition(node)) { - // TODO: fix + updated = true; return factory.updateClassDefinition( node, node.ident, @@ -215,108 +295,140 @@ export function visitEachChild( ); } if (isMethodDefinition(node)) { - // TODO: fix + updated = true; return factory.updateMethodDefinition( node, node.kind, node.name, - factory.createFunctionExpression( - // TODO: maybe fix - nodeVisitor(node.scriptFunction, visitor) - ), + nodeVisitor(node.scriptFunction, visitor), node.modifiers, false ); } - if (isScriptFunction(node)) { - return factory.updateScriptFunction( - node, - nodeVisitor(node.body, visitor), - FunctionSignature.createFunctionSignature( - nodeVisitor(node.typeParams, visitor), - nodesVisitor(node.params, visitor), - nodeVisitor(node.returnTypeAnnotation, visitor), - node.hasReceiver - ), - node.flags, - node.modifiers - ); + if (isTSInterfaceBody(node)) { + updated = true; + return factory.updateInterfaceBody(node, nodesVisitor(node.body, visitor)); } - if (isMemberExpression(node)) { - return factory.updateMemberExpression( + if (isVariableDeclarator(node)) { + updated = true; + return factory.updateVariableDeclarator( node, - nodeVisitor(node.object, visitor), - nodeVisitor(node.property, visitor), - node.kind, - node.computed, - node.optional + global.generatedEs2panda._VariableDeclaratorFlag(global.context, node.peer), + nodeVisitor(node.name, visitor), + nodeVisitor(node.initializer, visitor) ); } - if (isTSInterfaceDeclaration(node)) { - return factory.updateInterfaceDeclaration( - node, - nodesVisitor(node.extends, visitor), - nodeVisitor(node.id, visitor), - nodeVisitor(node.typeParams, visitor), - nodeVisitor(node.body, visitor), - node.isStatic, - // TODO: how do I get it? - true - ); + return node; +} + +function visitStatement(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; } - if (isTSInterfaceBody(node)) { - return factory.updateInterfaceBody( - node, - nodesVisitor(node.body, visitor) - ); + if (isBlockStatement(node)) { + updated = true; + return factory.updateBlock(node, nodesVisitor(node.statements, visitor)); + } + if (isExpressionStatement(node)) { + updated = true; + return factory.updateExpressionStatement(node, nodeVisitor(node.expression, visitor)); } if (isIfStatement(node)) { + updated = true; return factory.updateIfStatement( node, nodeVisitor(node.test, visitor), nodeVisitor(node.consequent, visitor), - nodeVisitor(node.alternate, visitor), + nodeVisitor(node.alternate, visitor) ); } - if (isConditionalExpression(node)) { - return factory.updateConditionalExpression( + if (isReturnStatement(node)) { + updated = true; + return factory.updateReturnStatement(node, nodeVisitor(node.argument, visitor)); + } + if (isTryStatement(node)) { + updated = true; + return factory.updateTryStatement( node, - nodeVisitor(node.test, visitor), - nodeVisitor(node.consequent, visitor), - nodeVisitor(node.alternate, visitor), + nodeVisitor(node.block, visitor), + nodesVisitor(node.catchClauses, visitor), + nodeVisitor(node.finallyBlock, visitor), + [], + [] ); } - if (isVariableDeclaration(node)) { - return factory.updateVariableDeclaration( + // TODO + return node; +} + +function visitForLoopStatement(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isForUpdateStatement(node)) { + updated = true; + return factory.updateForUpdateStatement( node, - 0, - node.declarationKind, - nodesVisitor(node.declarators, visitor), + nodeVisitor(node.init, visitor), + nodeVisitor(node.test, visitor), + nodeVisitor(node.update, visitor), + nodeVisitor(node.body, visitor) ); } - if (isVariableDeclarator(node)) { - return factory.updateVariableDeclarator( + if (isForInStatement(node)) { + updated = true; + return factory.updateForInStatement( node, - global.generatedEs2panda._VariableDeclaratorFlag(global.context, node.peer), - nodeVisitor(node.name, visitor), - nodeVisitor(node.initializer, visitor), + nodeVisitor(node.left, visitor), + nodeVisitor(node.right, visitor), + nodeVisitor(node.body, visitor) ); } - if (isArrowFunctionExpression(node)) { - return factory.updateArrowFunction( + if (isForOfStatement(node)) { + updated = true; + return factory.updateForOfStatement( node, - nodeVisitor(node.scriptFunction, visitor), + nodeVisitor(node.left, visitor), + nodeVisitor(node.right, visitor), + nodeVisitor(node.body, visitor), + node.isAwait ); } - if (isTSAsExpression(node)) { - return factory.updateTSAsExpression( + return node; +} + +function visitETSModule(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isEtsScript(node)) { + updated = true; + return factory.updateEtsScript(node, nodesVisitor(node.statements, visitor)); + } + return node; +} + +function visitDefinitionBody(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isScriptFunction(node)) { + updated = true; + return factory.updateScriptFunction( node, - nodeVisitor(node.expr, visitor), - nodeVisitor(node.typeAnnotation, visitor), - node.isConst + nodeVisitor(node.body, visitor), + factory.createFunctionSignature( + nodeVisitor(node.typeParams, visitor), + nodesVisitor(node.params, visitor), + nodeVisitor(node.returnTypeAnnotation, visitor), + node.hasReceiver + ), + node.flags, + node.modifiers ); } if (isClassProperty(node)) { + updated = true; return factory.updateClassProperty( node, node.key, @@ -326,21 +438,39 @@ export function visitEachChild( node.isComputed ); } - if (isClassProperty(node)) { - const _node = factory.updateClassProperty( + // TODO + return node; +} + +function visitLiteral(node: AstNode, visitor: Visitor): AstNode { + if (updated) { + return node; + } + if (isTemplateLiteral(node)) { + updated = true; + return factory.updateTemplateLiteral( node, - node.key, - nodeVisitor(node.value, visitor), - node.typeAnnotation, - node.modifiers, - node.isComputed + nodesVisitor(node.quasis, visitor), + nodesVisitor(node.expressions, visitor), + node.multilineString ); - if (hasModifierFlag(node, Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL)) { - classPropertySetOptional(_node, true); - } - _node.setAnnotations(node.annotations); - return _node; } - // TODO - return visitWithoutUpdate(node, visitor); + return node; +} + +// TODO: apply this to all nodes that does not require updating +function visitWithoutUpdate(node: T, visitor: Visitor): T { + if (updated) { + return node; + } + if (isImportDeclaration(node)) { + nodesVisitor(node.specifiers, visitor); + } + if (isETSFunctionType(node)) { + nodesVisitor(node.params, visitor); + } + if (isEtsParameterExpression(node)) { + nodeVisitor(node.type, visitor); + } + return node; } diff --git a/koala-wrapper/src/generated/Es2pandaEnums.ts b/koala-wrapper/src/generated/Es2pandaEnums.ts index cc682d5a37f8bd99386cefb141db22a17b42ed51..1e5ee0d2c482bbc7844c58134580b5f277e611cc 100644 --- a/koala-wrapper/src/generated/Es2pandaEnums.ts +++ b/koala-wrapper/src/generated/Es2pandaEnums.ts @@ -17,13 +17,12 @@ export enum Es2pandaContextState { ES2PANDA_STATE_NEW = 0, ES2PANDA_STATE_PARSED = 1, - ES2PANDA_STATE_SCOPE_INITED = 2, - ES2PANDA_STATE_BOUND = 3, - ES2PANDA_STATE_CHECKED = 4, - ES2PANDA_STATE_LOWERED = 5, - ES2PANDA_STATE_ASM_GENERATED = 6, - ES2PANDA_STATE_BIN_GENERATED = 7, - ES2PANDA_STATE_ERROR = 8 + ES2PANDA_STATE_BOUND = 2, + ES2PANDA_STATE_CHECKED = 3, + ES2PANDA_STATE_LOWERED = 4, + ES2PANDA_STATE_ASM_GENERATED = 5, + ES2PANDA_STATE_BIN_GENERATED = 6, + ES2PANDA_STATE_ERROR = 7 } // export enum Es2pandaAstNodeType { // AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION = 0, @@ -332,190 +331,189 @@ export enum Es2pandaId { ID_COUNT = 4 } export enum Es2pandaTokenType { - TOKEN_TYPE_EOS = 0, - TOKEN_TYPE_LITERAL_IDENT = 1, - TOKEN_TYPE_LITERAL_STRING = 2, - TOKEN_TYPE_LITERAL_CHAR = 3, - TOKEN_TYPE_LITERAL_NUMBER = 4, - TOKEN_TYPE_LITERAL_REGEXP = 5, - TOKEN_TYPE_PUNCTUATOR_BITWISE_AND = 6, - TOKEN_TYPE_PUNCTUATOR_BITWISE_OR = 7, - TOKEN_TYPE_PUNCTUATOR_MULTIPLY = 8, - TOKEN_TYPE_PUNCTUATOR_DIVIDE = 9, - TOKEN_TYPE_PUNCTUATOR_MINUS = 10, - TOKEN_TYPE_PUNCTUATOR_EXCLAMATION_MARK = 11, - TOKEN_TYPE_PUNCTUATOR_TILDE = 12, - TOKEN_TYPE_PUNCTUATOR_MINUS_MINUS = 13, - TOKEN_TYPE_PUNCTUATOR_LEFT_SHIFT = 14, - TOKEN_TYPE_PUNCTUATOR_RIGHT_SHIFT = 15, - TOKEN_TYPE_PUNCTUATOR_LESS_THAN_EQUAL = 16, - TOKEN_TYPE_PUNCTUATOR_GREATER_THAN_EQUAL = 17, - TOKEN_TYPE_PUNCTUATOR_MOD = 18, - TOKEN_TYPE_PUNCTUATOR_BITWISE_XOR = 19, - TOKEN_TYPE_PUNCTUATOR_EXPONENTIATION = 20, - TOKEN_TYPE_PUNCTUATOR_MULTIPLY_EQUAL = 21, - TOKEN_TYPE_PUNCTUATOR_EXPONENTIATION_EQUAL = 22, - TOKEN_TYPE_PUNCTUATOR_ARROW = 23, - TOKEN_TYPE_PUNCTUATOR_BACK_TICK = 24, - TOKEN_TYPE_PUNCTUATOR_HASH_MARK = 25, - TOKEN_TYPE_PUNCTUATOR_DIVIDE_EQUAL = 26, - TOKEN_TYPE_PUNCTUATOR_MOD_EQUAL = 27, - TOKEN_TYPE_PUNCTUATOR_MINUS_EQUAL = 28, - TOKEN_TYPE_PUNCTUATOR_LEFT_SHIFT_EQUAL = 29, - TOKEN_TYPE_PUNCTUATOR_RIGHT_SHIFT_EQUAL = 30, - TOKEN_TYPE_PUNCTUATOR_UNSIGNED_RIGHT_SHIFT = 31, - TOKEN_TYPE_PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL = 32, - TOKEN_TYPE_PUNCTUATOR_BITWISE_AND_EQUAL = 33, - TOKEN_TYPE_PUNCTUATOR_BITWISE_OR_EQUAL = 34, - TOKEN_TYPE_PUNCTUATOR_LOGICAL_AND_EQUAL = 35, - TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING = 36, - TOKEN_TYPE_PUNCTUATOR_LOGICAL_OR_EQUAL = 37, - TOKEN_TYPE_PUNCTUATOR_LOGICAL_NULLISH_EQUAL = 38, - TOKEN_TYPE_PUNCTUATOR_BITWISE_XOR_EQUAL = 39, - TOKEN_TYPE_PUNCTUATOR_PLUS = 40, - TOKEN_TYPE_PUNCTUATOR_PLUS_PLUS = 41, - TOKEN_TYPE_PUNCTUATOR_PLUS_EQUAL = 42, - TOKEN_TYPE_PUNCTUATOR_LESS_THAN = 43, - TOKEN_TYPE_PUNCTUATOR_GREATER_THAN = 44, - TOKEN_TYPE_PUNCTUATOR_EQUAL = 45, - TOKEN_TYPE_PUNCTUATOR_NOT_EQUAL = 46, - TOKEN_TYPE_PUNCTUATOR_STRICT_EQUAL = 47, - TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL = 48, - TOKEN_TYPE_PUNCTUATOR_LOGICAL_AND = 49, - TOKEN_TYPE_PUNCTUATOR_LOGICAL_OR = 50, - TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION = 51, - TOKEN_TYPE_PUNCTUATOR_QUESTION_MARK = 52, - TOKEN_TYPE_PUNCTUATOR_QUESTION_DOT = 53, - TOKEN_TYPE_PUNCTUATOR_AT = 54, - TOKEN_TYPE_PUNCTUATOR_FORMAT = 55, - TOKEN_TYPE_PUNCTUATOR_RIGHT_PARENTHESIS = 56, - TOKEN_TYPE_PUNCTUATOR_LEFT_PARENTHESIS = 57, - TOKEN_TYPE_PUNCTUATOR_RIGHT_SQUARE_BRACKET = 58, - TOKEN_TYPE_PUNCTUATOR_LEFT_SQUARE_BRACKET = 59, - TOKEN_TYPE_PUNCTUATOR_RIGHT_BRACE = 60, - TOKEN_TYPE_PUNCTUATOR_PERIOD = 61, - TOKEN_TYPE_PUNCTUATOR_PERIOD_PERIOD_PERIOD = 62, - TOKEN_TYPE_PUNCTUATOR_PERIOD_QUESTION = 63, - TOKEN_TYPE_PUNCTUATOR_LEFT_BRACE = 64, - TOKEN_TYPE_PUNCTUATOR_SEMI_COLON = 65, - TOKEN_TYPE_PUNCTUATOR_COLON = 66, - TOKEN_TYPE_PUNCTUATOR_COMMA = 67, - TOKEN_TYPE_KEYW_ABSTRACT = 68, - TOKEN_TYPE_KEYW_ANY = 69, - TOKEN_TYPE_KEYW_ANYREF = 70, - TOKEN_TYPE_KEYW_ARGUMENTS = 71, - TOKEN_TYPE_KEYW_AS = 72, - TOKEN_TYPE_KEYW_ASSERT = 73, - TOKEN_TYPE_KEYW_ASSERTS = 74, - TOKEN_TYPE_KEYW_ASYNC = 75, - TOKEN_TYPE_KEYW_AWAIT = 76, - TOKEN_TYPE_KEYW_BIGINT = 77, - TOKEN_TYPE_KEYW_BOOLEAN = 78, - TOKEN_TYPE_KEYW_BREAK = 79, - TOKEN_TYPE_KEYW_BYTE = 80, - TOKEN_TYPE_KEYW_CASE = 81, - TOKEN_TYPE_KEYW_CATCH = 82, - TOKEN_TYPE_KEYW_CHAR = 83, - TOKEN_TYPE_KEYW_CLASS = 84, - TOKEN_TYPE_KEYW_CONST = 85, - TOKEN_TYPE_KEYW_CONSTRUCTOR = 86, - TOKEN_TYPE_KEYW_CONTINUE = 87, - TOKEN_TYPE_KEYW_DATAREF = 88, - TOKEN_TYPE_KEYW_DEBUGGER = 89, - TOKEN_TYPE_KEYW_DECLARE = 90, - TOKEN_TYPE_KEYW_DEFAULT = 91, - TOKEN_TYPE_KEYW_DELETE = 92, - TOKEN_TYPE_KEYW_DO = 93, - TOKEN_TYPE_KEYW_DOUBLE = 94, - TOKEN_TYPE_KEYW_ELSE = 95, - TOKEN_TYPE_KEYW_ENUM = 96, - TOKEN_TYPE_KEYW_EQREF = 97, - TOKEN_TYPE_KEYW_EVAL = 98, - TOKEN_TYPE_KEYW_EXPORT = 99, - TOKEN_TYPE_KEYW_EXTENDS = 100, - TOKEN_TYPE_KEYW_EXTERNREF = 101, - TOKEN_TYPE_KEYW_F32 = 102, - TOKEN_TYPE_KEYW_F64 = 103, - TOKEN_TYPE_LITERAL_FALSE = 104, - TOKEN_TYPE_KEYW_FINALLY = 105, - TOKEN_TYPE_KEYW_FLOAT = 106, - TOKEN_TYPE_KEYW_FOR = 107, - TOKEN_TYPE_KEYW_FROM = 108, - TOKEN_TYPE_KEYW_FUNCREF = 109, - TOKEN_TYPE_KEYW_FUNCTION = 110, - TOKEN_TYPE_KEYW_GET = 111, - TOKEN_TYPE_KEYW_GLOBAL = 112, - TOKEN_TYPE_KEYW_I8 = 113, - TOKEN_TYPE_KEYW_I16 = 114, - TOKEN_TYPE_KEYW_I31REF = 115, - TOKEN_TYPE_KEYW_I32 = 116, - TOKEN_TYPE_KEYW_I64 = 117, - TOKEN_TYPE_KEYW_IF = 118, - TOKEN_TYPE_KEYW_IMPLEMENTS = 119, - TOKEN_TYPE_KEYW_IMPORT = 120, - TOKEN_TYPE_KEYW_IN = 121, - TOKEN_TYPE_KEYW_INFER = 122, - TOKEN_TYPE_KEYW_INSTANCEOF = 123, - TOKEN_TYPE_KEYW_INT = 124, - TOKEN_TYPE_KEYW_INTERFACE = 125, - TOKEN_TYPE_KEYW_IS = 126, - TOKEN_TYPE_KEYW_ISIZE = 127, - TOKEN_TYPE_KEYW_KEYOF = 128, - TOKEN_TYPE_KEYW_LET = 129, - TOKEN_TYPE_KEYW_LAUNCH = 130, - TOKEN_TYPE_KEYW_LONG = 131, - TOKEN_TYPE_KEYW_META = 132, - TOKEN_TYPE_KEYW_MODULE = 133, - TOKEN_TYPE_KEYW_NAMESPACE = 134, - TOKEN_TYPE_KEYW_NATIVE = 135, - TOKEN_TYPE_KEYW_NEVER = 136, - TOKEN_TYPE_KEYW_NEW = 137, - TOKEN_TYPE_LITERAL_NULL = 138, - TOKEN_TYPE_KEYW_NUMBER = 139, - TOKEN_TYPE_KEYW_OBJECT = 140, - TOKEN_TYPE_KEYW_OF = 141, - TOKEN_TYPE_KEYW_FINAL = 142, - TOKEN_TYPE_KEYW_OUT = 143, - TOKEN_TYPE_KEYW_OVERRIDE = 144, - TOKEN_TYPE_KEYW_PACKAGE = 145, - TOKEN_TYPE_KEYW_INTERNAL = 146, - TOKEN_TYPE_KEYW_PRIVATE = 147, - TOKEN_TYPE_KEYW_PROTECTED = 148, - TOKEN_TYPE_KEYW_PUBLIC = 149, - TOKEN_TYPE_KEYW_READONLY = 150, - TOKEN_TYPE_KEYW_RETHROWS = 151, - TOKEN_TYPE_KEYW_RETURN = 152, - TOKEN_TYPE_KEYW_REQUIRE = 153, - TOKEN_TYPE_KEYW_SET = 154, - TOKEN_TYPE_KEYW_SHORT = 155, - TOKEN_TYPE_KEYW_STATIC = 156, - TOKEN_TYPE_KEYW_STRING = 157, - TOKEN_TYPE_KEYW_STRUCT = 158, - TOKEN_TYPE_KEYW_SUPER = 159, - TOKEN_TYPE_KEYW_SWITCH = 160, - TOKEN_TYPE_KEYW_TARGET = 161, - TOKEN_TYPE_KEYW_THIS = 162, - TOKEN_TYPE_KEYW_THROW = 163, - TOKEN_TYPE_KEYW_THROWS = 164, - TOKEN_TYPE_LITERAL_TRUE = 165, - TOKEN_TYPE_KEYW_TRY = 166, - TOKEN_TYPE_KEYW_TYPE = 167, - TOKEN_TYPE_KEYW_TYPEOF = 168, - TOKEN_TYPE_KEYW_U8 = 169, - TOKEN_TYPE_KEYW_U16 = 170, - TOKEN_TYPE_KEYW_U32 = 171, - TOKEN_TYPE_KEYW_U64 = 172, - TOKEN_TYPE_KEYW_UNDEFINED = 173, - TOKEN_TYPE_KEYW_UNKNOWN = 174, - TOKEN_TYPE_KEYW_USIZE = 175, - TOKEN_TYPE_KEYW_V128 = 176, - TOKEN_TYPE_KEYW_VAR = 177, - TOKEN_TYPE_KEYW_VOID = 178, - TOKEN_TYPE_KEYW_WHILE = 179, - TOKEN_TYPE_KEYW_WITH = 180, - TOKEN_TYPE_KEYW_YIELD = 181, - TOKEN_TYPE_FIRST_PUNCTUATOR = 6, - TOKEN_TYPE_FIRST_KEYW = 68 + TOKEN_TYPE_EOS, + TOKEN_TYPE_LITERAL_IDENT, + TOKEN_TYPE_LITERAL_STRING, + TOKEN_TYPE_LITERAL_CHAR, + TOKEN_TYPE_LITERAL_NUMBER, + TOKEN_TYPE_LITERAL_REGEXP, + TOKEN_TYPE_PUNCTUATOR_BITWISE_AND, + TOKEN_TYPE_PUNCTUATOR_BITWISE_OR, + TOKEN_TYPE_PUNCTUATOR_MULTIPLY, + TOKEN_TYPE_PUNCTUATOR_DIVIDE, + TOKEN_TYPE_PUNCTUATOR_MINUS, + TOKEN_TYPE_PUNCTUATOR_EXCLAMATION_MARK, + TOKEN_TYPE_PUNCTUATOR_TILDE, + TOKEN_TYPE_PUNCTUATOR_MINUS_MINUS, + TOKEN_TYPE_PUNCTUATOR_LEFT_SHIFT, + TOKEN_TYPE_PUNCTUATOR_RIGHT_SHIFT, + TOKEN_TYPE_PUNCTUATOR_LESS_THAN_EQUAL, + TOKEN_TYPE_PUNCTUATOR_GREATER_THAN_EQUAL, + TOKEN_TYPE_PUNCTUATOR_MOD, + TOKEN_TYPE_PUNCTUATOR_BITWISE_XOR, + TOKEN_TYPE_PUNCTUATOR_EXPONENTIATION, + TOKEN_TYPE_PUNCTUATOR_MULTIPLY_EQUAL, + TOKEN_TYPE_PUNCTUATOR_EXPONENTIATION_EQUAL, + TOKEN_TYPE_PUNCTUATOR_ARROW, + TOKEN_TYPE_PUNCTUATOR_BACK_TICK, + TOKEN_TYPE_PUNCTUATOR_HASH_MARK, + TOKEN_TYPE_PUNCTUATOR_DIVIDE_EQUAL, + TOKEN_TYPE_PUNCTUATOR_MOD_EQUAL, + TOKEN_TYPE_PUNCTUATOR_MINUS_EQUAL, + TOKEN_TYPE_PUNCTUATOR_LEFT_SHIFT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_RIGHT_SHIFT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_UNSIGNED_RIGHT_SHIFT, + TOKEN_TYPE_PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_BITWISE_AND_EQUAL, + TOKEN_TYPE_PUNCTUATOR_BITWISE_OR_EQUAL, + TOKEN_TYPE_PUNCTUATOR_LOGICAL_AND_EQUAL, + TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING, + TOKEN_TYPE_PUNCTUATOR_LOGICAL_OR_EQUAL, + TOKEN_TYPE_PUNCTUATOR_LOGICAL_NULLISH_EQUAL, + TOKEN_TYPE_PUNCTUATOR_BITWISE_XOR_EQUAL, + TOKEN_TYPE_PUNCTUATOR_PLUS, + TOKEN_TYPE_PUNCTUATOR_PLUS_PLUS, + TOKEN_TYPE_PUNCTUATOR_PLUS_EQUAL, + TOKEN_TYPE_PUNCTUATOR_LESS_THAN, + TOKEN_TYPE_PUNCTUATOR_GREATER_THAN, + TOKEN_TYPE_PUNCTUATOR_EQUAL, + TOKEN_TYPE_PUNCTUATOR_NOT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_STRICT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL, + TOKEN_TYPE_PUNCTUATOR_LOGICAL_AND, + TOKEN_TYPE_PUNCTUATOR_LOGICAL_OR, + TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + TOKEN_TYPE_PUNCTUATOR_QUESTION_MARK, + TOKEN_TYPE_PUNCTUATOR_QUESTION_DOT, + TOKEN_TYPE_PUNCTUATOR_AT, + TOKEN_TYPE_PUNCTUATOR_FORMAT, + TOKEN_TYPE_PUNCTUATOR_RIGHT_PARENTHESIS, + TOKEN_TYPE_PUNCTUATOR_LEFT_PARENTHESIS, + TOKEN_TYPE_PUNCTUATOR_RIGHT_SQUARE_BRACKET, + TOKEN_TYPE_PUNCTUATOR_LEFT_SQUARE_BRACKET, + TOKEN_TYPE_PUNCTUATOR_RIGHT_BRACE, + TOKEN_TYPE_PUNCTUATOR_PERIOD, + TOKEN_TYPE_PUNCTUATOR_PERIOD_PERIOD_PERIOD, + TOKEN_TYPE_PUNCTUATOR_PERIOD_QUESTION, + TOKEN_TYPE_PUNCTUATOR_LEFT_BRACE, + TOKEN_TYPE_PUNCTUATOR_SEMI_COLON, + TOKEN_TYPE_PUNCTUATOR_COLON, + TOKEN_TYPE_PUNCTUATOR_COMMA, + TOKEN_TYPE_KEYW_ABSTRACT, + TOKEN_TYPE_KEYW_ANY, + TOKEN_TYPE_KEYW_ANYREF, + TOKEN_TYPE_KEYW_ARGUMENTS, + TOKEN_TYPE_KEYW_AS, + TOKEN_TYPE_KEYW_ASSERT, + TOKEN_TYPE_KEYW_ASSERTS, + TOKEN_TYPE_KEYW_ASYNC, + TOKEN_TYPE_KEYW_AWAIT, + TOKEN_TYPE_KEYW_BIGINT, + TOKEN_TYPE_KEYW_BOOLEAN, + TOKEN_TYPE_KEYW_BREAK, + TOKEN_TYPE_KEYW_BYTE, + TOKEN_TYPE_KEYW_CASE, + TOKEN_TYPE_KEYW_CATCH, + TOKEN_TYPE_KEYW_CHAR, + TOKEN_TYPE_KEYW_CLASS, + TOKEN_TYPE_KEYW_CONST, + TOKEN_TYPE_KEYW_CONSTRUCTOR, + TOKEN_TYPE_KEYW_CONTINUE, + TOKEN_TYPE_KEYW_DATAREF, + TOKEN_TYPE_KEYW_DEBUGGER, + TOKEN_TYPE_KEYW_DECLARE, + TOKEN_TYPE_KEYW_DEFAULT, + TOKEN_TYPE_KEYW_DELETE, + TOKEN_TYPE_KEYW_DO, + TOKEN_TYPE_KEYW_DOUBLE, + TOKEN_TYPE_KEYW_ELSE, + TOKEN_TYPE_KEYW_ENUM, + TOKEN_TYPE_KEYW_EQREF, + TOKEN_TYPE_KEYW_EVAL, + TOKEN_TYPE_KEYW_EXPORT, + TOKEN_TYPE_KEYW_EXTENDS, + TOKEN_TYPE_KEYW_EXTERNREF, + TOKEN_TYPE_KEYW_F32, + TOKEN_TYPE_KEYW_F64, + TOKEN_TYPE_LITERAL_FALSE, + TOKEN_TYPE_KEYW_FINALLY, + TOKEN_TYPE_KEYW_FLOAT, + TOKEN_TYPE_KEYW_FOR, + TOKEN_TYPE_KEYW_FROM, + TOKEN_TYPE_KEYW_FUNCREF, + TOKEN_TYPE_KEYW_FUNCTION, + TOKEN_TYPE_KEYW_GET, + TOKEN_TYPE_KEYW_GLOBAL, + TOKEN_TYPE_KEYW_I8, + TOKEN_TYPE_KEYW_I16, + TOKEN_TYPE_KEYW_I31REF, + TOKEN_TYPE_KEYW_I32, + TOKEN_TYPE_KEYW_I64, + TOKEN_TYPE_KEYW_IF, + TOKEN_TYPE_KEYW_IMPLEMENTS, + TOKEN_TYPE_KEYW_IMPORT, + TOKEN_TYPE_KEYW_IN, + TOKEN_TYPE_KEYW_INFER, + TOKEN_TYPE_KEYW_INSTANCEOF, + TOKEN_TYPE_KEYW_INT, + TOKEN_TYPE_KEYW_INTERFACE, + TOKEN_TYPE_KEYW_IS, + TOKEN_TYPE_KEYW_ISIZE, + TOKEN_TYPE_KEYW_KEYOF, + TOKEN_TYPE_KEYW_LET, + TOKEN_TYPE_KEYW_LONG, + TOKEN_TYPE_KEYW_META, + TOKEN_TYPE_KEYW_MODULE, + TOKEN_TYPE_KEYW_NAMESPACE, + TOKEN_TYPE_KEYW_NATIVE, + TOKEN_TYPE_KEYW_NEVER, + TOKEN_TYPE_KEYW_NEW, + TOKEN_TYPE_LITERAL_NULL, + TOKEN_TYPE_KEYW_NUMBER, + TOKEN_TYPE_KEYW_OBJECT, + TOKEN_TYPE_KEYW_OF, + TOKEN_TYPE_KEYW_FINAL, + TOKEN_TYPE_KEYW_OUT, + TOKEN_TYPE_KEYW_OVERRIDE, + TOKEN_TYPE_KEYW_PACKAGE, + TOKEN_TYPE_KEYW_INTERNAL, + TOKEN_TYPE_KEYW_PRIVATE, + TOKEN_TYPE_KEYW_PROTECTED, + TOKEN_TYPE_KEYW_PUBLIC, + TOKEN_TYPE_KEYW_READONLY, + TOKEN_TYPE_KEYW_RETHROWS, + TOKEN_TYPE_KEYW_RETURN, + TOKEN_TYPE_KEYW_REQUIRE, + TOKEN_TYPE_KEYW_SET, + TOKEN_TYPE_KEYW_SHORT, + TOKEN_TYPE_KEYW_STATIC, + TOKEN_TYPE_KEYW_STRING, + TOKEN_TYPE_KEYW_STRUCT, + TOKEN_TYPE_KEYW_SUPER, + TOKEN_TYPE_KEYW_SWITCH, + TOKEN_TYPE_KEYW_TARGET, + TOKEN_TYPE_KEYW_THIS, + TOKEN_TYPE_KEYW_THROW, + TOKEN_TYPE_KEYW_THROWS, + TOKEN_TYPE_LITERAL_TRUE, + TOKEN_TYPE_KEYW_TRY, + TOKEN_TYPE_KEYW_TYPE, + TOKEN_TYPE_KEYW_TYPEOF, + TOKEN_TYPE_KEYW_U8, + TOKEN_TYPE_KEYW_U16, + TOKEN_TYPE_KEYW_U32, + TOKEN_TYPE_KEYW_U64, + TOKEN_TYPE_KEYW_UNDEFINED, + TOKEN_TYPE_KEYW_UNKNOWN, + TOKEN_TYPE_KEYW_USIZE, + TOKEN_TYPE_KEYW_V128, + TOKEN_TYPE_KEYW_VAR, + TOKEN_TYPE_KEYW_VOID, + TOKEN_TYPE_KEYW_WHILE, + TOKEN_TYPE_KEYW_WITH, + TOKEN_TYPE_KEYW_YIELD, + TOKEN_TYPE_FIRST_PUNCTUATOR = Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_BITWISE_AND, + TOKEN_TYPE_FIRST_KEYW = Es2pandaTokenType.TOKEN_TYPE_KEYW_ABSTRACT } export enum Es2pandaAstNodeFlags { AST_NODE_FLAGS_NO_OPTS = 0, diff --git a/koala-wrapper/src/generated/Es2pandaNativeModule.ts b/koala-wrapper/src/generated/Es2pandaNativeModule.ts index 51040c02b05ba8742744f136b4f0b3712e0621f5..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") } @@ -2143,9 +2134,6 @@ export class Es2pandaNativeModule { _UpdateETSPackageDeclaration(context: KNativePointer, original: KNativePointer, name: KNativePointer): KNativePointer { throw new Error("'UpdateETSPackageDeclaration was not overloaded by native module initialization") } - _CreateETSImportDeclaration(context: KNativePointer, importPath: KNativePointer, specifiers: BigUint64Array, specifiersSequenceLength: KUInt, importKind: KInt): KNativePointer { - throw new Error("'CreateETSImportDeclaration was not overloaded by native module initialization") - } _UpdateETSImportDeclaration(context: KNativePointer, original: KNativePointer, source: KNativePointer, specifiers: BigUint64Array, specifiersSequenceLength: KUInt, importKind: KInt): KNativePointer { throw new Error("'UpdateETSImportDeclaration was not overloaded by native module initialization") } @@ -2155,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") } @@ -2311,18 +2296,6 @@ export class Es2pandaNativeModule { _UpdateEmptyStatement(context: KNativePointer, original: KNativePointer): KNativePointer { throw new Error("'UpdateEmptyStatement was not overloaded by native module initialization") } - _CreateETSLaunchExpression(context: KNativePointer, expr: KNativePointer): KNativePointer { - throw new Error("'CreateETSLaunchExpression was not overloaded by native module initialization") - } - _UpdateETSLaunchExpression(context: KNativePointer, original: KNativePointer, expr: KNativePointer): KNativePointer { - throw new Error("'UpdateETSLaunchExpression was not overloaded by native module initialization") - } - _ETSLaunchExpressionIsStaticCallConst(context: KNativePointer, receiver: KNativePointer): KBoolean { - throw new Error("'ETSLaunchExpressionIsStaticCallConst was not overloaded by native module initialization") - } - _ETSLaunchExpressionCallConst(context: KNativePointer, receiver: KNativePointer): KNativePointer { - throw new Error("'ETSLaunchExpressionCallConst was not overloaded by native module initialization") - } _CreateWhileStatement(context: KNativePointer, test: KNativePointer, body: KNativePointer): KNativePointer { throw new Error("'CreateWhileStatement was not overloaded by native module initialization") } @@ -3577,6 +3550,16 @@ export class Es2pandaNativeModule { _CreateForUpdateStatement(context: KNativePointer, init: KNativePointer, test: KNativePointer, update: KNativePointer, body: KNativePointer): KNativePointer { throw new Error("'CreateForUpdateStatement was not overloaded by native module initialization") } + _UpdateForUpdateStatement( + context: KNativePointer, + original: KNativePointer, + init: KNativePointer, + test: KNativePointer, + update: KNativePointer, + body: KNativePointer + ): KNativePointer { + throw new Error("'CreateForUpdateStatement was not overloaded by native module initialization"); + } _ForUpdateStatementInit(context: KNativePointer, receiver: KNativePointer): KNativePointer { throw new Error("'ForUpdateStatementInit was not overloaded by native module initialization") } @@ -3712,4 +3695,4 @@ export class Es2pandaNativeModule { _CreateFunctionDecl(context: KNativePointer, name: KStringPtr, node: KNativePointer): KNativePointer { throw new Error("'CreateFunctionDecl was not overloaded by native module initialization") } -} \ No newline at end of file +} diff --git a/koala-wrapper/src/generated/index.ts b/koala-wrapper/src/generated/index.ts index 4b9ae3fc28b965c76cd7f80ca2ea1717e1f8f944..81fc18f948a0803b92940947a2398ece77332177 100644 --- a/koala-wrapper/src/generated/index.ts +++ b/koala-wrapper/src/generated/index.ts @@ -106,7 +106,6 @@ export * from "./peers/LoopStatement" export * from "./peers/AnnotationDeclaration" export * from "./peers/AnnotationUsage" export * from "./peers/EmptyStatement" -export * from "./peers/ETSLaunchExpression" export * from "./peers/WhileStatement" export * from "./peers/FunctionSignature" export * from "./peers/ChainExpression" @@ -189,4 +188,5 @@ export * from "./peers/TSThisType" export * from "./peers/ETSDynamicFunctionType" export * from "./peers/InterfaceDecl" export * from "./peers/FunctionDecl" -export * from "./peers/Context" \ No newline at end of file +export * from "./peers/Context" +export * from "./peers/TSClassImplements"; diff --git a/koala-wrapper/src/generated/node-map.ts b/koala-wrapper/src/generated/node-map.ts index c8d89403908f48298837a6c28bbcb7ac8c158924..d9de6e3b5fecd3d79127458d5a3285f37529c442 100644 --- a/koala-wrapper/src/generated/node-map.ts +++ b/koala-wrapper/src/generated/node-map.ts @@ -27,156 +27,155 @@ import { import * as peers from "./index" export const nodes = new Map([ - [44, peers.LabelledStatement], - [146, peers.ThrowStatement], - [17, peers.ClassProperty], - [94, peers.TSVoidKeyword], - [66, peers.ETSFunctionType], - [117, peers.TSTypeOperator], - [38, peers.IfStatement], - [126, peers.TSConstructorType], - [22, peers.Decorator], - [87, peers.TSEnumDeclaration], - [99, peers.TSNeverKeyword], - [41, peers.ImportDefaultSpecifier], - [43, peers.ImportSpecifier], - [19, peers.ConditionalExpression], - [10, peers.CallExpression], - [5, peers.BigIntLiteral], - [112, peers.TSImportType], - [141, peers.TaggedTemplateExpression], - [34, peers.FunctionDeclaration], - [71, peers.ETSTypeReference], - [128, peers.TSTypeReference], - [48, peers.NamedType], - [52, peers.NumberLiteral], - [125, peers.TSFunctionType], - [142, peers.TemplateElement], - [131, peers.TSInterfaceDeclaration], - [150, peers.VariableDeclaration], - [51, peers.UndefinedLiteral], - [45, peers.MemberExpression], - [139, peers.TSClassImplements], - [97, peers.TSObjectKeyword], - [73, peers.ETSUnionType], - [105, peers.TSPropertySignature], - [111, peers.TSConditionalType], - [109, peers.TSLiteralType], - [127, peers.TSTypeAliasDeclaration], - [21, peers.DebuggerStatement], - [58, peers.ReturnStatement], - [27, peers.ExportDefaultDeclaration], - [59, peers.ScriptFunction], - [14, peers.ClassDefinition], - [132, peers.TSInterfaceBody], - [137, peers.TSTypeQuery], - [98, peers.TSBigintKeyword], - [55, peers.Property], - [151, peers.VariableDeclarator], - [61, peers.StringLiteral], - [140, peers.TSTypeAssertion], - [89, peers.TSExternalModuleReference], - [95, peers.TSUndefinedKeyword], - [81, peers.ETSTuple], - [147, peers.TryStatement], - [148, peers.UnaryExpression], - [31, peers.ForInStatement], - [144, peers.ThisExpression], - [106, peers.TSMethodSignature], - [6, peers.BinaryExpression], - [83, peers.SuperExpression], - [3, peers.AssertStatement], - [92, peers.TSStringKeyword], - [30, peers.ExpressionStatement], - [82, peers.ETSModule], - [46, peers.MetaProperty], - [102, peers.TSArrayType], - [107, peers.TSSignatureDeclaration], - [26, peers.ExportAllDeclaration], - [29, peers.ExportSpecifier], - [134, peers.TSTupleType], - [35, peers.FunctionExpression], - [136, peers.TSIndexSignature], - [123, peers.TSModuleDeclaration], - [39, peers.ImportDeclaration], - [108, peers.TSParenthesizedType], - [13, peers.CharLiteral], - [69, peers.ETSPackageDeclaration], - [79, peers.ETSImportDeclaration], - [84, peers.ETSStructDeclaration], - [115, peers.TSModuleBlock], - [76, peers.ETSNewArrayInstanceExpression], - [1, peers.AnnotationDeclaration], - [2, peers.AnnotationUsage], - [25, peers.EmptyStatement], - [75, peers.ETSLaunchExpression], - [152, peers.WhileStatement], - [12, peers.ChainExpression], - [113, peers.TSIntersectionType], - [149, peers.UpdateExpression], - [155, peers.BlockExpression], - [104, peers.TSTypeLiteral], - [118, peers.TSTypeParameter], - [93, peers.TSBooleanKeyword], - [121, peers.TSTypePredicate], - [42, peers.ImportNamespaceSpecifier], - [28, peers.ExportNamedDeclaration], - [80, peers.ETSParameterExpression], - [120, peers.TSTypeParameterInstantiation], - [50, peers.NullLiteral], - [110, peers.TSInferType], - [85, peers.SwitchCaseStatement], - [153, peers.YieldExpression], - [124, peers.TSImportEqualsDeclaration], - [8, peers.BooleanLiteral], - [90, peers.TSNumberKeyword], - [18, peers.ClassStaticBlock], - [100, peers.TSNonNullExpression], - [54, peers.PrefixAssertionExpression], - [16, peers.ClassExpression], - [32, peers.ForOfStatement], - [143, peers.TemplateLiteral], - [103, peers.TSUnionType], - [96, peers.TSUnknownKeyword], - [36, peers.Identifier], - [154, peers.OpaqueTypeNode], - [7, peers.BlockStatement], - [23, peers.DirectEvalExpression], - [119, peers.TSTypeParameterDeclaration], - [47, peers.MethodDefinition], - [101, peers.TSNullKeyword], - [133, peers.TSInterfaceHeritage], - [70, peers.ETSClassLiteral], - [9, peers.BreakStatement], - [56, peers.RegExpLiteral], - [114, peers.TSMappedType], - [91, peers.TSAnyKeyword], - [15, peers.ClassDeclaration], - [130, peers.TSIndexedAccessType], - [129, peers.TSQualifiedName], - [4, peers.AwaitExpression], - [20, peers.ContinueStatement], - [77, peers.ETSNewMultiDimArrayInstanceExpression], - [135, peers.TSNamedTupleMember], - [40, peers.ImportExpression], - [62, peers.ETSNullType], - [63, peers.ETSUndefinedType], - [145, peers.TypeofExpression], - [88, peers.TSEnumMember], - [86, peers.SwitchStatement], - [24, peers.DoWhileStatement], - [11, peers.CatchClause], - [60, peers.SequenceExpression], - [0, peers.ArrowFunctionExpression], - [53, peers.OmittedExpression], - [78, peers.ETSNewClassInstanceExpression], - [138, peers.TSAsExpression], - [33, peers.ForUpdateStatement], - [72, peers.ETSTypeReferencePart], - [57, peers.ETSReExportDeclaration], - [68, peers.ETSPrimitiveType], - [49, peers.NewExpression], - [122, peers.TSParameterProperty], - [67, peers.ETSWildcardType], - [116, peers.TSThisType], -]) \ No newline at end of file + [Es2pandaAstNodeType.AST_NODE_TYPE_LABELLED_STATEMENT, peers.LabelledStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_THROW_STATEMENT, peers.ThrowStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, peers.ClassProperty], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_VOID_KEYWORD, peers.TSVoidKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, peers.ETSFunctionType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_OPERATOR, peers.TSTypeOperator], + [Es2pandaAstNodeType.AST_NODE_TYPE_IF_STATEMENT, peers.IfStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE, peers.TSConstructorType], + [Es2pandaAstNodeType.AST_NODE_TYPE_DECORATOR, peers.Decorator], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_DECLARATION, peers.TSEnumDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_NEVER_KEYWORD, peers.TSNeverKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER, peers.ImportDefaultSpecifier], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_SPECIFIER, peers.ImportSpecifier], + [Es2pandaAstNodeType.AST_NODE_TYPE_CONDITIONAL_EXPRESSION, peers.ConditionalExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, peers.CallExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_BIGINT_LITERAL, peers.BigIntLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_TYPE, peers.TSImportType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION, peers.TaggedTemplateExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_DECLARATION, peers.FunctionDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE, peers.ETSTypeReference], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_REFERENCE, peers.TSTypeReference], + [Es2pandaAstNodeType.AST_NODE_TYPE_NAMED_TYPE, peers.NamedType], + [Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL, peers.NumberLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_FUNCTION_TYPE, peers.TSFunctionType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_ELEMENT, peers.TemplateElement], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_DECLARATION, peers.TSInterfaceDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATION, peers.VariableDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL, peers.UndefinedLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION, peers.MemberExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_CLASS_IMPLEMENTS, peers.TSClassImplements], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_OBJECT_KEYWORD, peers.TSObjectKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, peers.ETSUnionType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_PROPERTY_SIGNATURE, peers.TSPropertySignature], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONDITIONAL_TYPE, peers.TSConditionalType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE, peers.TSLiteralType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, peers.TSTypeAliasDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_DEBUGGER_STATEMENT, peers.DebuggerStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, peers.ReturnStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION, peers.ExportDefaultDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, peers.ScriptFunction], + [Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION, peers.ClassDefinition], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_BODY, peers.TSInterfaceBody], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_QUERY, peers.TSTypeQuery], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_BIGINT_KEYWORD, peers.TSBigintKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, peers.Property], + [Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR, peers.VariableDeclarator], + [Es2pandaAstNodeType.AST_NODE_TYPE_STRING_LITERAL, peers.StringLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ASSERTION, peers.TSTypeAssertion], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE, peers.TSExternalModuleReference], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNDEFINED_KEYWORD, peers.TSUndefinedKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TUPLE, peers.ETSTuple], + [Es2pandaAstNodeType.AST_NODE_TYPE_TRY_STATEMENT, peers.TryStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_UNARY_EXPRESSION, peers.UnaryExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_FOR_IN_STATEMENT, peers.ForInStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_THIS_EXPRESSION, peers.ThisExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_METHOD_SIGNATURE, peers.TSMethodSignature], + [Es2pandaAstNodeType.AST_NODE_TYPE_BINARY_EXPRESSION, peers.BinaryExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION, peers.SuperExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ASSERT_STATEMENT, peers.AssertStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_STRING_KEYWORD, peers.TSStringKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_EXPRESSION_STATEMENT, peers.ExpressionStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE, peers.ETSModule], + [Es2pandaAstNodeType.AST_NODE_TYPE_META_PROPERTY_EXPRESSION, peers.MetaProperty], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_ARRAY_TYPE, peers.TSArrayType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_SIGNATURE_DECLARATION, peers.TSSignatureDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_ALL_DECLARATION, peers.ExportAllDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_SPECIFIER, peers.ExportSpecifier], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TUPLE_TYPE, peers.TSTupleType], + [Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION, peers.FunctionExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEX_SIGNATURE, peers.TSIndexSignature], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_DECLARATION, peers.TSModuleDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DECLARATION, peers.ImportDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARENT_TYPE, peers.TSParenthesizedType], + [Es2pandaAstNodeType.AST_NODE_TYPE_CHAR_LITERAL, peers.CharLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PACKAGE_DECLARATION, peers.ETSPackageDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DECLARATION, peers.ETSImportDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_STRUCT_DECLARATION, peers.ETSStructDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_BLOCK, peers.TSModuleBlock], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION, peers.ETSNewArrayInstanceExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_DECLARATION, peers.AnnotationDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_USAGE, peers.AnnotationUsage], + [Es2pandaAstNodeType.AST_NODE_TYPE_EMPTY_STATEMENT, peers.EmptyStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_WHILE_STATEMENT, peers.WhileStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_CHAIN_EXPRESSION, peers.ChainExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERSECTION_TYPE, peers.TSIntersectionType], + [Es2pandaAstNodeType.AST_NODE_TYPE_UPDATE_EXPRESSION, peers.UpdateExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_EXPRESSION, peers.BlockExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_LITERAL, peers.TSTypeLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER, peers.TSTypeParameter], + [Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL, peers.TSBooleanKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PREDICATE, peers.TSTypePredicate], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER, peers.ImportNamespaceSpecifier], + [Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_NAMED_DECLARATION, peers.ExportNamedDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, peers.ETSParameterExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION, peers.TSTypeParameterInstantiation], + [Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL, peers.NullLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INFER_TYPE, peers.TSInferType], + [Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_CASE_STATEMENT, peers.SwitchCaseStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_YIELD_EXPRESSION, peers.YieldExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION, peers.TSImportEqualsDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL, peers.BooleanLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_NUMBER_KEYWORD, peers.TSNumberKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, peers.ClassStaticBlock], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_NON_NULL_EXPRESSION, peers.TSNonNullExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION, peers.PrefixAssertionExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_EXPRESSION, peers.ClassExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_FOR_OF_STATEMENT, peers.ForOfStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_LITERAL, peers.TemplateLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNION_TYPE, peers.TSUnionType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNKNOWN_KEYWORD, peers.TSUnknownKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, peers.Identifier], + [Es2pandaAstNodeType.AST_NODE_TYPE_OPAQUE_TYPE_NODE, peers.OpaqueTypeNode], + [Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT, peers.BlockStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_DIRECT_EVAL, peers.DirectEvalExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION, peers.TSTypeParameterDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, peers.MethodDefinition], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_NULL_KEYWORD, peers.TSNullKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_HERITAGE, peers.TSInterfaceHeritage], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_CLASS_LITERAL, peers.ETSClassLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_BREAK_STATEMENT, peers.BreakStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_REGEXP_LITERAL, peers.RegExpLiteral], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_MAPPED_TYPE, peers.TSMappedType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_ANY_KEYWORD, peers.TSAnyKeyword], + [Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DECLARATION, peers.ClassDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE, peers.TSIndexedAccessType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_QUALIFIED_NAME, peers.TSQualifiedName], + [Es2pandaAstNodeType.AST_NODE_TYPE_AWAIT_EXPRESSION, peers.AwaitExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_CONTINUE_STATEMENT, peers.ContinueStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION, peers.ETSNewMultiDimArrayInstanceExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER, peers.TSNamedTupleMember], + [Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_EXPRESSION, peers.ImportExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE, peers.ETSNullType], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNDEFINED_TYPE, peers.ETSUndefinedType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TYPEOF_EXPRESSION, peers.TypeofExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_MEMBER, peers.TSEnumMember], + [Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_STATEMENT, peers.SwitchStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_DO_WHILE_STATEMENT, peers.DoWhileStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_CATCH_CLAUSE, peers.CatchClause], + [Es2pandaAstNodeType.AST_NODE_TYPE_SEQUENCE_EXPRESSION, peers.SequenceExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, peers.ArrowFunctionExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_OMITTED_EXPRESSION, peers.OmittedExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION, peers.ETSNewClassInstanceExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_AS_EXPRESSION, peers.TSAsExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_FOR_UPDATE_STATEMENT, peers.ForUpdateStatement], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART, peers.ETSTypeReferencePart], + [Es2pandaAstNodeType.AST_NODE_TYPE_REEXPORT_STATEMENT, peers.ETSReExportDeclaration], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PRIMITIVE_TYPE, peers.ETSPrimitiveType], + [Es2pandaAstNodeType.AST_NODE_TYPE_NEW_EXPRESSION, peers.NewExpression], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARAMETER_PROPERTY, peers.TSParameterProperty], + [Es2pandaAstNodeType.AST_NODE_TYPE_ETS_WILDCARD_TYPE, peers.ETSWildcardType], + [Es2pandaAstNodeType.AST_NODE_TYPE_TS_THIS_TYPE, peers.TSThisType], +]) diff --git a/koala-wrapper/src/generated/peers/AnnotationUsage.ts b/koala-wrapper/src/generated/peers/AnnotationUsage.ts index 1ee1f98b75e00a865cbc3e915ab31253fe17fcb5..e093c461716a1b43409617ec6f2f3b739f5a61ac 100644 --- a/koala-wrapper/src/generated/peers/AnnotationUsage.ts +++ b/koala-wrapper/src/generated/peers/AnnotationUsage.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { Identifier } from "./Identifier" export class AnnotationUsage extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 2) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_USAGE) super(pointer) } @@ -73,6 +73,6 @@ export class AnnotationUsage extends Statement { export function isAnnotationUsage(node: AstNode): node is AnnotationUsage { return node instanceof AnnotationUsage } -if (!nodeByType.has(2)) { - nodeByType.set(2, AnnotationUsage) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_USAGE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_USAGE, AnnotationUsage) +} diff --git a/koala-wrapper/src/generated/peers/ArrayExpression.ts b/koala-wrapper/src/generated/peers/ArrayExpression.ts index 200648cdbbfed8e9caca4c075e0e858e722d84a1..717ce5f4566a0f4171cff61d4cebc610dcf6cac6 100644 --- a/koala-wrapper/src/generated/peers/ArrayExpression.ts +++ b/koala-wrapper/src/generated/peers/ArrayExpression.ts @@ -26,55 +26,81 @@ import { KNativePointer, nodeByType, ArktsObject, - unpackString -} from "../../reexport-for-generated" + unpackString, +} from '../../reexport-for-generated'; -import { AnnotatedExpression } from "./AnnotatedExpression" -import { Expression } from "./Expression" -import { Decorator } from "./Decorator" -import { ValidationInfo } from "./ValidationInfo" -import { TypeNode } from "./TypeNode" +import { AnnotatedExpression } from './AnnotatedExpression'; +import { Expression } from './Expression'; +import { Decorator } from './Decorator'; +import { ValidationInfo } from './ValidationInfo'; +import { TypeNode } from './TypeNode'; export class ArrayExpression extends AnnotatedExpression { - constructor(pointer: KNativePointer) { - super(pointer) - + constructor(pointer: KNativePointer) { + super(pointer); + } + static createArrayExpression(elements: readonly Expression[]): ArrayExpression { + return new ArrayExpression( + global.generatedEs2panda._CreateArrayExpression(global.context, passNodeArray(elements), elements.length) + ); + } + static updateArrayExpression( + original: ArrayExpression | undefined, + elements: readonly Expression[] + ): ArrayExpression { + return new ArrayExpression( + global.generatedEs2panda._UpdateArrayExpression( + global.context, + passNode(original), + passNodeArray(elements), + elements.length + ) + ); } get elements(): readonly Expression[] { - return unpackNodeArray(global.generatedEs2panda._ArrayExpressionElementsConst(global.context, this.peer)) + return unpackNodeArray(global.generatedEs2panda._ArrayExpressionElementsConst(global.context, this.peer)); } /** @deprecated */ setElements(elements: readonly Expression[]): this { - global.generatedEs2panda._ArrayExpressionSetElements(global.context, this.peer, passNodeArray(elements), elements.length) - return this + global.generatedEs2panda._ArrayExpressionSetElements( + global.context, + this.peer, + passNodeArray(elements), + elements.length + ); + return this; } get isDeclaration(): boolean { - return global.generatedEs2panda._ArrayExpressionIsDeclarationConst(global.context, this.peer) + return global.generatedEs2panda._ArrayExpressionIsDeclarationConst(global.context, this.peer); } get isOptional(): boolean { - return global.generatedEs2panda._ArrayExpressionIsOptionalConst(global.context, this.peer) + return global.generatedEs2panda._ArrayExpressionIsOptionalConst(global.context, this.peer); } /** @deprecated */ setDeclaration(): this { - global.generatedEs2panda._ArrayExpressionSetDeclaration(global.context, this.peer) - return this + global.generatedEs2panda._ArrayExpressionSetDeclaration(global.context, this.peer); + return this; } /** @deprecated */ setOptional(optional_arg: boolean): this { - global.generatedEs2panda._ArrayExpressionSetOptional(global.context, this.peer, optional_arg) - return this + global.generatedEs2panda._ArrayExpressionSetOptional(global.context, this.peer, optional_arg); + return this; } get decorators(): readonly Decorator[] { - return unpackNodeArray(global.generatedEs2panda._ArrayExpressionDecoratorsConst(global.context, this.peer)) + return unpackNodeArray(global.generatedEs2panda._ArrayExpressionDecoratorsConst(global.context, this.peer)); } get typeAnnotation(): TypeNode | undefined { - return unpackNode(global.generatedEs2panda._ArrayExpressionTypeAnnotationConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ArrayExpressionTypeAnnotationConst(global.context, this.peer)); } /** @deprecated */ setTsTypeAnnotation(typeAnnotation: TypeNode): this { - global.generatedEs2panda._ArrayExpressionSetTsTypeAnnotation(global.context, this.peer, passNode(typeAnnotation)) - return this + global.generatedEs2panda._ArrayExpressionSetTsTypeAnnotation( + global.context, + this.peer, + passNode(typeAnnotation) + ); + return this; } } export function isArrayExpression(node: AstNode): node is ArrayExpression { - return node instanceof ArrayExpression -} \ No newline at end of file + return global.es2panda._IsArrayExpression(node.peer); +} diff --git a/koala-wrapper/src/generated/peers/ArrowFunctionExpression.ts b/koala-wrapper/src/generated/peers/ArrowFunctionExpression.ts index 7c8ca00f58048bb39a0c19fccf3ce40fe939d779..75393b0377a5c00fc74361733e6279f44edc040d 100644 --- a/koala-wrapper/src/generated/peers/ArrowFunctionExpression.ts +++ b/koala-wrapper/src/generated/peers/ArrowFunctionExpression.ts @@ -35,7 +35,7 @@ import { TypeNode } from "./TypeNode" import { AnnotationUsage } from "./AnnotationUsage" export class ArrowFunctionExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 0) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION) super(pointer) } @@ -66,6 +66,6 @@ export class ArrowFunctionExpression extends Expression { export function isArrowFunctionExpression(node: AstNode): node is ArrowFunctionExpression { return node instanceof ArrowFunctionExpression } -if (!nodeByType.has(0)) { - nodeByType.set(0, ArrowFunctionExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, ArrowFunctionExpression) +} diff --git a/koala-wrapper/src/generated/peers/AssertStatement.ts b/koala-wrapper/src/generated/peers/AssertStatement.ts index 0d2dcfe058f7f7270dad0b8bc9bc8d817f1ab220..07f40d7c157bb33908f6fa1518cf365371774421 100644 --- a/koala-wrapper/src/generated/peers/AssertStatement.ts +++ b/koala-wrapper/src/generated/peers/AssertStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class AssertStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 3) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ASSERT_STATEMENT) super(pointer) } @@ -53,6 +53,6 @@ export class AssertStatement extends Statement { export function isAssertStatement(node: AstNode): node is AssertStatement { return node instanceof AssertStatement } -if (!nodeByType.has(3)) { - nodeByType.set(3, AssertStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ASSERT_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ASSERT_STATEMENT, AssertStatement) +} diff --git a/koala-wrapper/src/generated/peers/AwaitExpression.ts b/koala-wrapper/src/generated/peers/AwaitExpression.ts index 1e5520228a08f9d0bb41253daaf143a3cef7ef45..b090abc87530c6e50e7cd1dda1467308e90386d2 100644 --- a/koala-wrapper/src/generated/peers/AwaitExpression.ts +++ b/koala-wrapper/src/generated/peers/AwaitExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class AwaitExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 4) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_AWAIT_EXPRESSION) super(pointer) } @@ -49,6 +49,6 @@ export class AwaitExpression extends Expression { export function isAwaitExpression(node: AstNode): node is AwaitExpression { return node instanceof AwaitExpression } -if (!nodeByType.has(4)) { - nodeByType.set(4, AwaitExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_AWAIT_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_AWAIT_EXPRESSION, AwaitExpression) +} diff --git a/koala-wrapper/src/generated/peers/BigIntLiteral.ts b/koala-wrapper/src/generated/peers/BigIntLiteral.ts index 9d390c0c40c5afca38334b128b01374efe08d632..e03909d855f04e2944f083cd8b21707640bf16dc 100644 --- a/koala-wrapper/src/generated/peers/BigIntLiteral.ts +++ b/koala-wrapper/src/generated/peers/BigIntLiteral.ts @@ -32,7 +32,7 @@ import { import { Literal } from "./Literal" export class BigIntLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 5) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BIGINT_LITERAL) super(pointer) } @@ -49,6 +49,6 @@ export class BigIntLiteral extends Literal { export function isBigIntLiteral(node: AstNode): node is BigIntLiteral { return node instanceof BigIntLiteral } -if (!nodeByType.has(5)) { - nodeByType.set(5, BigIntLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BIGINT_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BIGINT_LITERAL, BigIntLiteral) +} diff --git a/koala-wrapper/src/generated/peers/BinaryExpression.ts b/koala-wrapper/src/generated/peers/BinaryExpression.ts index c524075b136e0da4ebc3d004b01c352700fbca6a..b911d0c588853aa18c80c7cb3b84023a419a38f9 100644 --- a/koala-wrapper/src/generated/peers/BinaryExpression.ts +++ b/koala-wrapper/src/generated/peers/BinaryExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Es2pandaTokenType } from "./../Es2pandaEnums" export class BinaryExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 6) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BINARY_EXPRESSION) super(pointer) } @@ -91,6 +91,6 @@ export class BinaryExpression extends Expression { export function isBinaryExpression(node: AstNode): node is BinaryExpression { return node instanceof BinaryExpression } -if (!nodeByType.has(6)) { - nodeByType.set(6, BinaryExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BINARY_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BINARY_EXPRESSION, BinaryExpression) +} diff --git a/koala-wrapper/src/generated/peers/BlockExpression.ts b/koala-wrapper/src/generated/peers/BlockExpression.ts index 7e22eef679aee156cd2d4ec307251d4ce7c8485f..38520bcd38a74752b02ae9e94929924a42598546 100644 --- a/koala-wrapper/src/generated/peers/BlockExpression.ts +++ b/koala-wrapper/src/generated/peers/BlockExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Statement } from "./Statement" export class BlockExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 155) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_EXPRESSION) super(pointer) } @@ -60,6 +60,6 @@ export class BlockExpression extends Expression { export function isBlockExpression(node: AstNode): node is BlockExpression { return node instanceof BlockExpression } -if (!nodeByType.has(155)) { - nodeByType.set(155, BlockExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_EXPRESSION, BlockExpression) +} diff --git a/koala-wrapper/src/generated/peers/BlockStatement.ts b/koala-wrapper/src/generated/peers/BlockStatement.ts index 8d8ddf9faa75180e6987f32fbaa3e4b8c7879f80..09948d77c80c4ab2477b811c9c3b73c31d586f89 100644 --- a/koala-wrapper/src/generated/peers/BlockStatement.ts +++ b/koala-wrapper/src/generated/peers/BlockStatement.ts @@ -32,7 +32,7 @@ import { import { Statement } from "./Statement" export class BlockStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 7) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT) super(pointer) } @@ -59,6 +59,6 @@ export class BlockStatement extends Statement { export function isBlockStatement(node: AstNode): node is BlockStatement { return node instanceof BlockStatement } -if (!nodeByType.has(7)) { - nodeByType.set(7, BlockStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT, BlockStatement) +} diff --git a/koala-wrapper/src/generated/peers/BooleanLiteral.ts b/koala-wrapper/src/generated/peers/BooleanLiteral.ts index 9995a9f899ca9b204a7f40a4d1923ca2a7a7da48..f82d3a6ede25a278b861804ccd661f39b991831f 100644 --- a/koala-wrapper/src/generated/peers/BooleanLiteral.ts +++ b/koala-wrapper/src/generated/peers/BooleanLiteral.ts @@ -32,7 +32,7 @@ import { import { Literal } from "./Literal" export class BooleanLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 8) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL) super(pointer) } @@ -49,6 +49,6 @@ export class BooleanLiteral extends Literal { export function isBooleanLiteral(node: AstNode): node is BooleanLiteral { return node instanceof BooleanLiteral } -if (!nodeByType.has(8)) { - nodeByType.set(8, BooleanLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BOOLEAN_LITERAL, BooleanLiteral) +} diff --git a/koala-wrapper/src/generated/peers/BreakStatement.ts b/koala-wrapper/src/generated/peers/BreakStatement.ts index e293ddf59a84d9b4bf0d78d972b5bb9f575451ae..77b609b0aea2d5e4d35b8bf9079ad885953432f4 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, 9) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_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(9)) { - nodeByType.set(9, BreakStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_BLOCK_STATEMENT, BreakStatement) +} diff --git a/koala-wrapper/src/generated/peers/CallExpression.ts b/koala-wrapper/src/generated/peers/CallExpression.ts index 3a61208f9c3d4f191378568cc26fa5d46eceb8d4..2a84226657d93fa783c6b8a6c8a472decbc61ebb 100644 --- a/koala-wrapper/src/generated/peers/CallExpression.ts +++ b/koala-wrapper/src/generated/peers/CallExpression.ts @@ -35,7 +35,7 @@ import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" import { BlockStatement } from "./BlockStatement" export class CallExpression extends MaybeOptionalExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 10) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION) super(pointer) } @@ -93,6 +93,6 @@ export class CallExpression extends MaybeOptionalExpression { export function isCallExpression(node: AstNode): node is CallExpression { return node instanceof CallExpression } -if (!nodeByType.has(10)) { - nodeByType.set(10, CallExpression) -} \ No newline at end of file +if (!nodeByType.has( Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION)) { + nodeByType.set( Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, CallExpression) +} diff --git a/koala-wrapper/src/generated/peers/CatchClause.ts b/koala-wrapper/src/generated/peers/CatchClause.ts index 068b1dd80b8c99f82db9ee1407f6455394df6c05..270650213efa466bea3f14f01cead87c604d9b8e 100644 --- a/koala-wrapper/src/generated/peers/CatchClause.ts +++ b/koala-wrapper/src/generated/peers/CatchClause.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { BlockStatement } from "./BlockStatement" export class CatchClause extends TypedStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 11) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CATCH_CLAUSE) super(pointer) } @@ -54,6 +54,6 @@ export class CatchClause extends TypedStatement { export function isCatchClause(node: AstNode): node is CatchClause { return node instanceof CatchClause } -if (!nodeByType.has(11)) { - nodeByType.set(11, CatchClause) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CATCH_CLAUSE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CATCH_CLAUSE, CatchClause) +} diff --git a/koala-wrapper/src/generated/peers/ChainExpression.ts b/koala-wrapper/src/generated/peers/ChainExpression.ts index ae76f46e0606112f0078a568c51e35fc1a1b5fc0..2c89dcd538649c014c7c14fc375f5d03c8435f33 100644 --- a/koala-wrapper/src/generated/peers/ChainExpression.ts +++ b/koala-wrapper/src/generated/peers/ChainExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class ChainExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 12) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CHAIN_EXPRESSION) super(pointer) } @@ -49,6 +49,6 @@ export class ChainExpression extends Expression { export function isChainExpression(node: AstNode): node is ChainExpression { return node instanceof ChainExpression } -if (!nodeByType.has(12)) { - nodeByType.set(12, ChainExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CHAIN_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CHAIN_EXPRESSION, ChainExpression) +} diff --git a/koala-wrapper/src/generated/peers/CharLiteral.ts b/koala-wrapper/src/generated/peers/CharLiteral.ts index 7dbceda4ca686d7495b696fcb6b6d98d9a0e4e7f..97c67d44e6438b2ab3ff424ba0c9828801bcd24f 100644 --- a/koala-wrapper/src/generated/peers/CharLiteral.ts +++ b/koala-wrapper/src/generated/peers/CharLiteral.ts @@ -32,7 +32,7 @@ import { import { Literal } from "./Literal" export class CharLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 13) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CHAR_LITERAL) super(pointer) } @@ -46,6 +46,6 @@ export class CharLiteral extends Literal { export function isCharLiteral(node: AstNode): node is CharLiteral { return node instanceof CharLiteral } -if (!nodeByType.has(13)) { - nodeByType.set(13, CharLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CHAR_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CHAR_LITERAL, CharLiteral) +} diff --git a/koala-wrapper/src/generated/peers/ClassDeclaration.ts b/koala-wrapper/src/generated/peers/ClassDeclaration.ts index 1af101f1c2f6d0545c3268ecf45c737821a9b5e9..f163c2de3d69d0731af82b8d499af20fd5f9abe1 100644 --- a/koala-wrapper/src/generated/peers/ClassDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ClassDeclaration.ts @@ -34,7 +34,7 @@ import { ClassDefinition } from "./ClassDefinition" import { Decorator } from "./Decorator" export class ClassDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 15) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DECLARATION) super(pointer) } @@ -54,6 +54,6 @@ export class ClassDeclaration extends Statement { export function isClassDeclaration(node: AstNode): node is ClassDeclaration { return node instanceof ClassDeclaration } -if (!nodeByType.has(15)) { - nodeByType.set(15, ClassDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DECLARATION, ClassDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ClassDefinition.ts b/koala-wrapper/src/generated/peers/ClassDefinition.ts index f1187001ddc5834e67dd26e80bf99ec379c8b6ab..fe51f912be9594aa2f104ce7e9172eaedd3c7141 100644 --- a/koala-wrapper/src/generated/peers/ClassDefinition.ts +++ b/koala-wrapper/src/generated/peers/ClassDefinition.ts @@ -44,7 +44,7 @@ import { FunctionExpression } from "./FunctionExpression" import { AnnotationUsage } from "./AnnotationUsage" export class ClassDefinition extends TypedAstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 14) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION) super(pointer) } @@ -217,6 +217,6 @@ export class ClassDefinition extends TypedAstNode { export function isClassDefinition(node: AstNode): node is ClassDefinition { return node instanceof ClassDefinition } -if (!nodeByType.has(14)) { - nodeByType.set(14, ClassDefinition) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_DEFINITION, ClassDefinition) +} diff --git a/koala-wrapper/src/generated/peers/ClassExpression.ts b/koala-wrapper/src/generated/peers/ClassExpression.ts index 9b2f05b2ee27b171562b7dc5201f3a2eaee66fa3..e0e0bfef8c7366e3a98e588ac3056a37a0db15e1 100644 --- a/koala-wrapper/src/generated/peers/ClassExpression.ts +++ b/koala-wrapper/src/generated/peers/ClassExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { ClassDefinition } from "./ClassDefinition" export class ClassExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 16) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_EXPRESSION) super(pointer) } @@ -50,6 +50,6 @@ export class ClassExpression extends Expression { export function isClassExpression(node: AstNode): node is ClassExpression { return node instanceof ClassExpression } -if (!nodeByType.has(16)) { - nodeByType.set(16, ClassExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_EXPRESSION, ClassExpression) +} diff --git a/koala-wrapper/src/generated/peers/ClassProperty.ts b/koala-wrapper/src/generated/peers/ClassProperty.ts index cac674bfbb21e1068eb0e12d63f1a1f941828d2d..04648bd9994fc6c286dfbd5ac341456991f808cb 100644 --- a/koala-wrapper/src/generated/peers/ClassProperty.ts +++ b/koala-wrapper/src/generated/peers/ClassProperty.ts @@ -36,7 +36,7 @@ import { Es2pandaModifierFlags } from "./../Es2pandaEnums" import { AnnotationUsage } from "./AnnotationUsage" export class ClassProperty extends ClassElement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 17) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY) super(pointer) } @@ -66,6 +66,6 @@ export class ClassProperty extends ClassElement { export function isClassProperty(node: AstNode): node is ClassProperty { return node instanceof ClassProperty } -if (!nodeByType.has(17)) { - nodeByType.set(17, ClassProperty) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, ClassProperty) +} diff --git a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts index 92ff36429e53715f18d423241b28399d9c3b8398..5b2bef593b039d4915de3c85060394e62c60b6c2 100644 --- a/koala-wrapper/src/generated/peers/ClassStaticBlock.ts +++ b/koala-wrapper/src/generated/peers/ClassStaticBlock.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { ScriptFunction } from "./ScriptFunction" export class ClassStaticBlock extends ClassElement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 18) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK) super(pointer) } @@ -48,6 +48,6 @@ export class ClassStaticBlock extends ClassElement { export function isClassStaticBlock(node: AstNode): node is ClassStaticBlock { return node instanceof ClassStaticBlock } -if (!nodeByType.has(18)) { - nodeByType.set(18, ClassStaticBlock) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_STATIC_BLOCK, ClassStaticBlock) +} diff --git a/koala-wrapper/src/generated/peers/ConditionalExpression.ts b/koala-wrapper/src/generated/peers/ConditionalExpression.ts index 20ab3b02825d4ef50fba2a174773fbe3e57210b5..998aa934da8854bebfaffc0f87e74ec06957d265 100644 --- a/koala-wrapper/src/generated/peers/ConditionalExpression.ts +++ b/koala-wrapper/src/generated/peers/ConditionalExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class ConditionalExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 19) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CONDITIONAL_EXPRESSION) super(pointer) } @@ -70,6 +70,6 @@ export class ConditionalExpression extends Expression { export function isConditionalExpression(node: AstNode): node is ConditionalExpression { return node instanceof ConditionalExpression } -if (!nodeByType.has(19)) { - nodeByType.set(19, ConditionalExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CONDITIONAL_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CONDITIONAL_EXPRESSION, ConditionalExpression) +} diff --git a/koala-wrapper/src/generated/peers/ContinueStatement.ts b/koala-wrapper/src/generated/peers/ContinueStatement.ts index 6d9f8a42e195eacd5dc3da03fe57fc9606e7e41d..52cd73c0d461d1324f2b169c8f436a3c48975492 100644 --- a/koala-wrapper/src/generated/peers/ContinueStatement.ts +++ b/koala-wrapper/src/generated/peers/ContinueStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class ContinueStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 20) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_CONTINUE_STATEMENT) super(pointer) } @@ -64,6 +64,6 @@ export class ContinueStatement extends Statement { export function isContinueStatement(node: AstNode): node is ContinueStatement { return node instanceof ContinueStatement } -if (!nodeByType.has(20)) { - nodeByType.set(20, ContinueStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_CONTINUE_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_CONTINUE_STATEMENT, ContinueStatement) +} diff --git a/koala-wrapper/src/generated/peers/DebuggerStatement.ts b/koala-wrapper/src/generated/peers/DebuggerStatement.ts index e74c35108500dcbe15d385077c3df5cf092b4e0a..7bebfba19ad084074eb70526bb28b257f05c25c8 100644 --- a/koala-wrapper/src/generated/peers/DebuggerStatement.ts +++ b/koala-wrapper/src/generated/peers/DebuggerStatement.ts @@ -32,7 +32,7 @@ import { import { Statement } from "./Statement" export class DebuggerStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 21) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_DEBUGGER_STATEMENT) super(pointer) } @@ -46,6 +46,6 @@ export class DebuggerStatement extends Statement { export function isDebuggerStatement(node: AstNode): node is DebuggerStatement { return node instanceof DebuggerStatement } -if (!nodeByType.has(21)) { - nodeByType.set(21, DebuggerStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_DEBUGGER_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_DEBUGGER_STATEMENT, DebuggerStatement) +} diff --git a/koala-wrapper/src/generated/peers/Decorator.ts b/koala-wrapper/src/generated/peers/Decorator.ts index de635c11e0bf7a05c89c3d70e69ab9880a170e64..4f336655103721de26d2a37a33a946ca16c2e10b 100644 --- a/koala-wrapper/src/generated/peers/Decorator.ts +++ b/koala-wrapper/src/generated/peers/Decorator.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class Decorator extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 22) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_DECORATOR) super(pointer) } @@ -50,6 +50,6 @@ export class Decorator extends Statement { export function isDecorator(node: AstNode): node is Decorator { return node instanceof Decorator } -if (!nodeByType.has(22)) { - nodeByType.set(22, Decorator) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_DECORATOR)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_DECORATOR, Decorator) +} diff --git a/koala-wrapper/src/generated/peers/DirectEvalExpression.ts b/koala-wrapper/src/generated/peers/DirectEvalExpression.ts index d432f9f0d7f75da4891e873c3ac1431daecfb547..0accb0b24827c0154152400b5f4a9b3a02a7a76f 100644 --- a/koala-wrapper/src/generated/peers/DirectEvalExpression.ts +++ b/koala-wrapper/src/generated/peers/DirectEvalExpression.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class DirectEvalExpression extends CallExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 23) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_DIRECT_EVAL) super(pointer) } @@ -48,6 +48,6 @@ export class DirectEvalExpression extends CallExpression { export function isDirectEvalExpression(node: AstNode): node is DirectEvalExpression { return node instanceof DirectEvalExpression } -if (!nodeByType.has(23)) { - nodeByType.set(23, DirectEvalExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_DIRECT_EVAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_DIRECT_EVAL, DirectEvalExpression) +} diff --git a/koala-wrapper/src/generated/peers/DoWhileStatement.ts b/koala-wrapper/src/generated/peers/DoWhileStatement.ts index c6ce8c8cdd75525f236954870cf4b9d073f7cb30..569c03d1fd8452240781b5543e495b6b5b63a2cc 100644 --- a/koala-wrapper/src/generated/peers/DoWhileStatement.ts +++ b/koala-wrapper/src/generated/peers/DoWhileStatement.ts @@ -34,7 +34,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class DoWhileStatement extends LoopStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 24) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_DO_WHILE_STATEMENT) super(pointer) } @@ -54,6 +54,6 @@ export class DoWhileStatement extends LoopStatement { export function isDoWhileStatement(node: AstNode): node is DoWhileStatement { return node instanceof DoWhileStatement } -if (!nodeByType.has(24)) { - nodeByType.set(24, DoWhileStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_DO_WHILE_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_DO_WHILE_STATEMENT, DoWhileStatement) +} diff --git a/koala-wrapper/src/generated/peers/ETSClassLiteral.ts b/koala-wrapper/src/generated/peers/ETSClassLiteral.ts index d78af3b90973dfe0752b022beb31d0f288ee771e..80d2e86a75ad8fee0d83cfffd4e78233a5f6af13 100644 --- a/koala-wrapper/src/generated/peers/ETSClassLiteral.ts +++ b/koala-wrapper/src/generated/peers/ETSClassLiteral.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class ETSClassLiteral extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 70) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_CLASS_LITERAL) super(pointer) } @@ -50,6 +50,6 @@ export class ETSClassLiteral extends Expression { export function isETSClassLiteral(node: AstNode): node is ETSClassLiteral { return node instanceof ETSClassLiteral } -if (!nodeByType.has(70)) { - nodeByType.set(70, ETSClassLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_CLASS_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_CLASS_LITERAL, ETSClassLiteral) +} diff --git a/koala-wrapper/src/generated/peers/ETSFunctionType.ts b/koala-wrapper/src/generated/peers/ETSFunctionType.ts index 5354546f87ab4a341c4a8be71fc05e5c9b33b0d1..ed8d5f7edbc15944587b1f85ce9d074519cc606f 100644 --- a/koala-wrapper/src/generated/peers/ETSFunctionType.ts +++ b/koala-wrapper/src/generated/peers/ETSFunctionType.ts @@ -37,7 +37,7 @@ import { Expression } from "./Expression" import { TSInterfaceDeclaration } from "./TSInterfaceDeclaration" export class ETSFunctionType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 66) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE) super(pointer) } @@ -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) } @@ -80,6 +74,6 @@ export class ETSFunctionType extends TypeNode { export function isETSFunctionType(node: AstNode): node is ETSFunctionType { return node instanceof ETSFunctionType } -if (!nodeByType.has(66)) { - nodeByType.set(66, ETSFunctionType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, ETSFunctionType) +} diff --git a/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts b/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts index 9261c82c6ece6806621341d7396b24d0e902d480..919394d2ab7089a6335718a296ed630d625a7ed3 100644 --- a/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ETSImportDeclaration.ts @@ -33,14 +33,15 @@ import { ImportDeclaration } from "./ImportDeclaration" import { ImportSource } from "./ImportSource" import { Es2pandaImportKinds } from "./../Es2pandaEnums" import { StringLiteral } from "./StringLiteral" +import { Es2pandaImportFlags } from "./../../Es2pandaEnums" export class ETSImportDeclaration extends ImportDeclaration { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 79) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION) super(pointer) } - static createETSImportDeclaration(source: StringLiteral | undefined, specifiers: readonly AstNode[], importKind: Es2pandaImportKinds): ETSImportDeclaration { - return new ETSImportDeclaration(global.generatedEs2panda._CreateETSImportDeclaration(global.context, passNode(source), passNodeArray(specifiers), specifiers.length, importKind)) + static createETSImportDeclaration(source: StringLiteral | undefined, specifiers: readonly AstNode[], importKind: Es2pandaImportKinds, program: ArktsObject, flags: Es2pandaImportFlags): ETSImportDeclaration { + return new ETSImportDeclaration(global.es2panda._CreateETSImportDeclaration(global.context, passNode(source), passNodeArray(specifiers), specifiers.length, importKind, passNode(program), flags)) } static updateETSImportDeclaration(original: ETSImportDeclaration | undefined, source: StringLiteral | undefined, specifiers: readonly AstNode[], importKind: Es2pandaImportKinds): ETSImportDeclaration { return new ETSImportDeclaration(global.generatedEs2panda._UpdateETSImportDeclaration(global.context, passNode(original), passNode(source), passNodeArray(specifiers), specifiers.length, importKind)) @@ -64,6 +65,6 @@ export class ETSImportDeclaration extends ImportDeclaration { export function isETSImportDeclaration(node: AstNode): node is ETSImportDeclaration { return node instanceof ETSImportDeclaration } -if (!nodeByType.has(79)) { - nodeByType.set(79, ETSImportDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_IMPORT_DECLARATION, ETSImportDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ETSModule.ts b/koala-wrapper/src/generated/peers/ETSModule.ts index df693d3b1544b6ee31d2b87ffbbd3e0c68c96182..396edaac792f8c1f58309b6e793cefbcee497188 100644 --- a/koala-wrapper/src/generated/peers/ETSModule.ts +++ b/koala-wrapper/src/generated/peers/ETSModule.ts @@ -34,7 +34,7 @@ import { Identifier } from "./Identifier" import { AnnotationUsage } from "./AnnotationUsage" export class ETSModule extends BlockStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 82) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) super(pointer) } @@ -67,6 +67,6 @@ export class ETSModule extends BlockStatement { export function isETSModule(node: AstNode): node is ETSModule { return node instanceof ETSModule } -if (!nodeByType.has(82)) { - nodeByType.set(82, ETSModule) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE, ETSModule) +} diff --git a/koala-wrapper/src/generated/peers/ETSNewArrayInstanceExpression.ts b/koala-wrapper/src/generated/peers/ETSNewArrayInstanceExpression.ts index 05135647e2cb1badbf59731376bc1d2c4afb87b0..63d66f2301a4a4c29c612c5dda64f0af03944bfd 100644 --- a/koala-wrapper/src/generated/peers/ETSNewArrayInstanceExpression.ts +++ b/koala-wrapper/src/generated/peers/ETSNewArrayInstanceExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class ETSNewArrayInstanceExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 76) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION) super(pointer) } @@ -58,6 +58,6 @@ export class ETSNewArrayInstanceExpression extends Expression { export function isETSNewArrayInstanceExpression(node: AstNode): node is ETSNewArrayInstanceExpression { return node instanceof ETSNewArrayInstanceExpression } -if (!nodeByType.has(76)) { - nodeByType.set(76, ETSNewArrayInstanceExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_ARRAY_INSTANCE_EXPRESSION, ETSNewArrayInstanceExpression) +} diff --git a/koala-wrapper/src/generated/peers/ETSNewClassInstanceExpression.ts b/koala-wrapper/src/generated/peers/ETSNewClassInstanceExpression.ts index 7e0bb389b7ec340a474d2095ad1224f71d5a130c..a79deafb32ff2802e889dfd9652d947b816d23c5 100644 --- a/koala-wrapper/src/generated/peers/ETSNewClassInstanceExpression.ts +++ b/koala-wrapper/src/generated/peers/ETSNewClassInstanceExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class ETSNewClassInstanceExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 78) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION) super(pointer) } @@ -68,6 +68,6 @@ export class ETSNewClassInstanceExpression extends Expression { export function isETSNewClassInstanceExpression(node: AstNode): node is ETSNewClassInstanceExpression { return node instanceof ETSNewClassInstanceExpression } -if (!nodeByType.has(78)) { - nodeByType.set(78, ETSNewClassInstanceExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_CLASS_INSTANCE_EXPRESSION, ETSNewClassInstanceExpression) +} diff --git a/koala-wrapper/src/generated/peers/ETSNewMultiDimArrayInstanceExpression.ts b/koala-wrapper/src/generated/peers/ETSNewMultiDimArrayInstanceExpression.ts index e99e3ed42814b3bb52642f4d7f02c7a0f004b101..5b599dce6b7e49a1c6dada39f26374501cf295b4 100644 --- a/koala-wrapper/src/generated/peers/ETSNewMultiDimArrayInstanceExpression.ts +++ b/koala-wrapper/src/generated/peers/ETSNewMultiDimArrayInstanceExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class ETSNewMultiDimArrayInstanceExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 77) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION) super(pointer) } @@ -59,6 +59,6 @@ export class ETSNewMultiDimArrayInstanceExpression extends Expression { export function isETSNewMultiDimArrayInstanceExpression(node: AstNode): node is ETSNewMultiDimArrayInstanceExpression { return node instanceof ETSNewMultiDimArrayInstanceExpression } -if (!nodeByType.has(77)) { - nodeByType.set(77, ETSNewMultiDimArrayInstanceExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NEW_MULTI_DIM_ARRAY_INSTANCE_EXPRESSION, ETSNewMultiDimArrayInstanceExpression) +} diff --git a/koala-wrapper/src/generated/peers/ETSNullType.ts b/koala-wrapper/src/generated/peers/ETSNullType.ts index edf561cf150b1d9aa2e53d1ee86b40ae64317721..6619bf72ac5a185fa077c4b1a2bf73a6e1665a26 100644 --- a/koala-wrapper/src/generated/peers/ETSNullType.ts +++ b/koala-wrapper/src/generated/peers/ETSNullType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class ETSNullType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 62) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE) super(pointer) } @@ -46,6 +46,6 @@ export class ETSNullType extends TypeNode { export function isETSNullType(node: AstNode): node is ETSNullType { return node instanceof ETSNullType } -if (!nodeByType.has(62)) { - nodeByType.set(62, ETSNullType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_NULL_TYPE, ETSNullType) +} diff --git a/koala-wrapper/src/generated/peers/ETSPackageDeclaration.ts b/koala-wrapper/src/generated/peers/ETSPackageDeclaration.ts index 1148df88e3ebc1f78e74338f740b40b32a384b47..dce8c626184ed386361f712522be7a7dcf253634 100644 --- a/koala-wrapper/src/generated/peers/ETSPackageDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ETSPackageDeclaration.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class ETSPackageDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 69) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PACKAGE_DECLARATION) super(pointer) } @@ -47,6 +47,6 @@ export class ETSPackageDeclaration extends Statement { export function isETSPackageDeclaration(node: AstNode): node is ETSPackageDeclaration { return node instanceof ETSPackageDeclaration } -if (!nodeByType.has(69)) { - nodeByType.set(69, ETSPackageDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PACKAGE_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PACKAGE_DECLARATION, ETSPackageDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ETSParameterExpression.ts b/koala-wrapper/src/generated/peers/ETSParameterExpression.ts index 3b8c76a41b9e668417b6000cc9e9c10dbb5b4029..7a16dc8e13e9c859a02d37cb49fb9e5dd7ee6e27 100644 --- a/koala-wrapper/src/generated/peers/ETSParameterExpression.ts +++ b/koala-wrapper/src/generated/peers/ETSParameterExpression.ts @@ -37,7 +37,7 @@ import { TypeNode } from "./TypeNode" import { AnnotationUsage } from "./AnnotationUsage" export class ETSParameterExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 80) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION) super(pointer) } @@ -104,6 +104,6 @@ export class ETSParameterExpression extends Expression { export function isETSParameterExpression(node: AstNode): node is ETSParameterExpression { return node instanceof ETSParameterExpression } -if (!nodeByType.has(80)) { - nodeByType.set(80, ETSParameterExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, ETSParameterExpression) +} diff --git a/koala-wrapper/src/generated/peers/ETSPrimitiveType.ts b/koala-wrapper/src/generated/peers/ETSPrimitiveType.ts index 82713f44b793ec034908e036efde6bd9ca025aa0..6d433dff4331cae02c739a27e6ed6269935576e5 100644 --- a/koala-wrapper/src/generated/peers/ETSPrimitiveType.ts +++ b/koala-wrapper/src/generated/peers/ETSPrimitiveType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Es2pandaPrimitiveType } from "./../Es2pandaEnums" export class ETSPrimitiveType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 68) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PRIMITIVE_TYPE) super(pointer) } @@ -50,6 +50,6 @@ export class ETSPrimitiveType extends TypeNode { export function isETSPrimitiveType(node: AstNode): node is ETSPrimitiveType { return node instanceof ETSPrimitiveType } -if (!nodeByType.has(68)) { - nodeByType.set(68, ETSPrimitiveType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PRIMITIVE_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PRIMITIVE_TYPE, ETSPrimitiveType) +} diff --git a/koala-wrapper/src/generated/peers/ETSReExportDeclaration.ts b/koala-wrapper/src/generated/peers/ETSReExportDeclaration.ts index 6e33931db2cdb761dd41ec645c1008db8eb445b6..669a54af1806e6109464a6067d141bbf2e021bd1 100644 --- a/koala-wrapper/src/generated/peers/ETSReExportDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ETSReExportDeclaration.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { ETSImportDeclaration } from "./ETSImportDeclaration" export class ETSReExportDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 57) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_REEXPORT_STATEMENT) super(pointer) } @@ -47,6 +47,6 @@ export class ETSReExportDeclaration extends Statement { export function isETSReExportDeclaration(node: AstNode): node is ETSReExportDeclaration { return node instanceof ETSReExportDeclaration } -if (!nodeByType.has(57)) { - nodeByType.set(57, ETSReExportDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_REEXPORT_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_REEXPORT_STATEMENT, ETSReExportDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ETSStructDeclaration.ts b/koala-wrapper/src/generated/peers/ETSStructDeclaration.ts index 9a060fdce6e49c4fce123763f57fd51d81de68d2..dfa10cf4ca9d80fb64449fa3dbef13433585834a 100644 --- a/koala-wrapper/src/generated/peers/ETSStructDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ETSStructDeclaration.ts @@ -33,7 +33,7 @@ import { ClassDeclaration } from "./ClassDeclaration" import { ClassDefinition } from "./ClassDefinition" export class ETSStructDeclaration extends ClassDeclaration { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 84) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_STRUCT_DECLARATION) super(pointer) } @@ -47,6 +47,6 @@ export class ETSStructDeclaration extends ClassDeclaration { export function isETSStructDeclaration(node: AstNode): node is ETSStructDeclaration { return node instanceof ETSStructDeclaration } -if (!nodeByType.has(84)) { - nodeByType.set(84, ETSStructDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_STRUCT_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_STRUCT_DECLARATION, ETSStructDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ETSTuple.ts b/koala-wrapper/src/generated/peers/ETSTuple.ts index 158eee3f8ef660ef5525786df6d3048c6d91e7e6..8eee89765c63aece54c8356386b339d3ca4f0ced 100644 --- a/koala-wrapper/src/generated/peers/ETSTuple.ts +++ b/koala-wrapper/src/generated/peers/ETSTuple.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class ETSTuple extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 81) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TUPLE) super(pointer) } @@ -77,6 +77,6 @@ export class ETSTuple extends TypeNode { export function isETSTuple(node: AstNode): node is ETSTuple { return node instanceof ETSTuple } -if (!nodeByType.has(81)) { - nodeByType.set(81, ETSTuple) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TUPLE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TUPLE, ETSTuple) +} diff --git a/koala-wrapper/src/generated/peers/ETSTypeReference.ts b/koala-wrapper/src/generated/peers/ETSTypeReference.ts index f18f4c8b33e4bb7c0633558e45cb5a09ed8af26c..1089a11ab3f6375513356bdc8f1b48f00f6f4355 100644 --- a/koala-wrapper/src/generated/peers/ETSTypeReference.ts +++ b/koala-wrapper/src/generated/peers/ETSTypeReference.ts @@ -34,7 +34,7 @@ import { ETSTypeReferencePart } from "./ETSTypeReferencePart" import { Identifier } from "./Identifier" export class ETSTypeReference extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 71) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE) super(pointer) } @@ -51,6 +51,6 @@ export class ETSTypeReference extends TypeNode { export function isETSTypeReference(node: AstNode): node is ETSTypeReference { return node instanceof ETSTypeReference } -if (!nodeByType.has(71)) { - nodeByType.set(71, ETSTypeReference) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE, ETSTypeReference) +} diff --git a/koala-wrapper/src/generated/peers/ETSTypeReferencePart.ts b/koala-wrapper/src/generated/peers/ETSTypeReferencePart.ts index 804642d3095963586e2cbe9abb2affba0932ca84..1a7f1968f0e24d19158191f3fb9349c96bb26353 100644 --- a/koala-wrapper/src/generated/peers/ETSTypeReferencePart.ts +++ b/koala-wrapper/src/generated/peers/ETSTypeReferencePart.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class ETSTypeReferencePart extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 72) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART) super(pointer) } @@ -63,6 +63,6 @@ export class ETSTypeReferencePart extends TypeNode { export function isETSTypeReferencePart(node: AstNode): node is ETSTypeReferencePart { return node instanceof ETSTypeReferencePart } -if (!nodeByType.has(72)) { - nodeByType.set(72, ETSTypeReferencePart) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART, ETSTypeReferencePart) +} diff --git a/koala-wrapper/src/generated/peers/ETSUndefinedType.ts b/koala-wrapper/src/generated/peers/ETSUndefinedType.ts index 6f44b6a87d57d63fa83e752dbdf3227361634973..c42dcf76b76f4ae0d7f02a217ece34d7f1dff2f5 100644 --- a/koala-wrapper/src/generated/peers/ETSUndefinedType.ts +++ b/koala-wrapper/src/generated/peers/ETSUndefinedType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class ETSUndefinedType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 63) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNDEFINED_TYPE) super(pointer) } @@ -46,6 +46,6 @@ export class ETSUndefinedType extends TypeNode { export function isETSUndefinedType(node: AstNode): node is ETSUndefinedType { return node instanceof ETSUndefinedType } -if (!nodeByType.has(63)) { - nodeByType.set(63, ETSUndefinedType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNDEFINED_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNDEFINED_TYPE, ETSUndefinedType) +} diff --git a/koala-wrapper/src/generated/peers/ETSUnionType.ts b/koala-wrapper/src/generated/peers/ETSUnionType.ts index 18ea1f14283272cd38aa5d8f38300c4afc32444b..59b73ebe7e7d0df4857e84112a98586aa949acfc 100644 --- a/koala-wrapper/src/generated/peers/ETSUnionType.ts +++ b/koala-wrapper/src/generated/peers/ETSUnionType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class ETSUnionType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 73) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE) super(pointer) } @@ -49,6 +49,6 @@ export class ETSUnionType extends TypeNode { export function isETSUnionType(node: AstNode): node is ETSUnionType { return node instanceof ETSUnionType } -if (!nodeByType.has(73)) { - nodeByType.set(73, ETSUnionType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, ETSUnionType) +} diff --git a/koala-wrapper/src/generated/peers/ETSWildcardType.ts b/koala-wrapper/src/generated/peers/ETSWildcardType.ts index 7dd790f553e8b31cde3d33f29835023d9bed0cc0..c34909ca718f7778049861118ccea66147249ae4 100644 --- a/koala-wrapper/src/generated/peers/ETSWildcardType.ts +++ b/koala-wrapper/src/generated/peers/ETSWildcardType.ts @@ -34,7 +34,7 @@ import { ETSTypeReference } from "./ETSTypeReference" import { Es2pandaModifierFlags } from "./../Es2pandaEnums" export class ETSWildcardType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 67) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_WILDCARD_TYPE) super(pointer) } @@ -51,6 +51,6 @@ export class ETSWildcardType extends TypeNode { export function isETSWildcardType(node: AstNode): node is ETSWildcardType { return node instanceof ETSWildcardType } -if (!nodeByType.has(67)) { - nodeByType.set(67, ETSWildcardType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_WILDCARD_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_ETS_WILDCARD_TYPE, ETSWildcardType) +} diff --git a/koala-wrapper/src/generated/peers/EmptyStatement.ts b/koala-wrapper/src/generated/peers/EmptyStatement.ts index 9e3e617064354d10895e72fb01e809757efe1ca3..b0b5e74f69af197a998ee8b5d6e2da431aaf76f3 100644 --- a/koala-wrapper/src/generated/peers/EmptyStatement.ts +++ b/koala-wrapper/src/generated/peers/EmptyStatement.ts @@ -32,7 +32,7 @@ import { import { Statement } from "./Statement" export class EmptyStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 25) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EMPTY_STATEMENT) super(pointer) } @@ -46,6 +46,6 @@ export class EmptyStatement extends Statement { export function isEmptyStatement(node: AstNode): node is EmptyStatement { return node instanceof EmptyStatement } -if (!nodeByType.has(25)) { - nodeByType.set(25, EmptyStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EMPTY_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EMPTY_STATEMENT, EmptyStatement) +} diff --git a/koala-wrapper/src/generated/peers/ExportAllDeclaration.ts b/koala-wrapper/src/generated/peers/ExportAllDeclaration.ts index 631cdc354baef48e39676b4c7a5014e02ef7ce71..b95b4268712bff444d95ae4f3e1726fe8a5e46de 100644 --- a/koala-wrapper/src/generated/peers/ExportAllDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ExportAllDeclaration.ts @@ -34,7 +34,7 @@ import { StringLiteral } from "./StringLiteral" import { Identifier } from "./Identifier" export class ExportAllDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 26) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_ALL_DECLARATION) super(pointer) } @@ -54,6 +54,6 @@ export class ExportAllDeclaration extends Statement { export function isExportAllDeclaration(node: AstNode): node is ExportAllDeclaration { return node instanceof ExportAllDeclaration } -if (!nodeByType.has(26)) { - nodeByType.set(26, ExportAllDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_ALL_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_ALL_DECLARATION, ExportAllDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ExportDefaultDeclaration.ts b/koala-wrapper/src/generated/peers/ExportDefaultDeclaration.ts index d4f07bb62af87674d0e843189ebc134de9bd2e4e..0a16008865dc8c0b86575a9fcc618ea9f2213543 100644 --- a/koala-wrapper/src/generated/peers/ExportDefaultDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ExportDefaultDeclaration.ts @@ -32,7 +32,7 @@ import { import { Statement } from "./Statement" export class ExportDefaultDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 27) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION) super(pointer) } @@ -52,6 +52,6 @@ export class ExportDefaultDeclaration extends Statement { export function isExportDefaultDeclaration(node: AstNode): node is ExportDefaultDeclaration { return node instanceof ExportDefaultDeclaration } -if (!nodeByType.has(27)) { - nodeByType.set(27, ExportDefaultDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_DEFAULT_DECLARATION, ExportDefaultDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ExportNamedDeclaration.ts b/koala-wrapper/src/generated/peers/ExportNamedDeclaration.ts index d7f82f76acbbc7ec52119503192901f769b1ce93..8fe0ed5939951c5d87b7d062a499cabb7c49a302 100644 --- a/koala-wrapper/src/generated/peers/ExportNamedDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ExportNamedDeclaration.ts @@ -34,7 +34,7 @@ import { StringLiteral } from "./StringLiteral" import { ExportSpecifier } from "./ExportSpecifier" export class ExportNamedDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 28) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_NAMED_DECLARATION) super(pointer) } @@ -69,6 +69,6 @@ export class ExportNamedDeclaration extends Statement { export function isExportNamedDeclaration(node: AstNode): node is ExportNamedDeclaration { return node instanceof ExportNamedDeclaration } -if (!nodeByType.has(28)) { - nodeByType.set(28, ExportNamedDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_NAMED_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_NAMED_DECLARATION, ExportNamedDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ExportSpecifier.ts b/koala-wrapper/src/generated/peers/ExportSpecifier.ts index de59281d958059f1a05f17cf64b3026d06675328..d68740c1a92adb9cfbe03409871282f62a1cb593 100644 --- a/koala-wrapper/src/generated/peers/ExportSpecifier.ts +++ b/koala-wrapper/src/generated/peers/ExportSpecifier.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class ExportSpecifier extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 29) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_SPECIFIER) super(pointer) } @@ -53,6 +53,6 @@ export class ExportSpecifier extends Statement { export function isExportSpecifier(node: AstNode): node is ExportSpecifier { return node instanceof ExportSpecifier } -if (!nodeByType.has(29)) { - nodeByType.set(29, ExportSpecifier) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_SPECIFIER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EXPORT_SPECIFIER, ExportSpecifier) +} diff --git a/koala-wrapper/src/generated/peers/ExpressionStatement.ts b/koala-wrapper/src/generated/peers/ExpressionStatement.ts index 6603e673eedcfc3fb7843cf4b17b1edc3f069a5e..d7f6ec665240fbdb9bef3c89608667cb9cc890ad 100644 --- a/koala-wrapper/src/generated/peers/ExpressionStatement.ts +++ b/koala-wrapper/src/generated/peers/ExpressionStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class ExpressionStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 30) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_EXPRESSION_STATEMENT) super(pointer) } @@ -55,6 +55,6 @@ export class ExpressionStatement extends Statement { export function isExpressionStatement(node: AstNode): node is ExpressionStatement { return node instanceof ExpressionStatement } -if (!nodeByType.has(30)) { - nodeByType.set(30, ExpressionStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_EXPRESSION_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_EXPRESSION_STATEMENT, ExpressionStatement) +} diff --git a/koala-wrapper/src/generated/peers/ForInStatement.ts b/koala-wrapper/src/generated/peers/ForInStatement.ts index ff051e35bb49ce64be1c459fbd377410168b7441..52d828bc18144096e4a20c57bfce70a5a8f991e8 100644 --- a/koala-wrapper/src/generated/peers/ForInStatement.ts +++ b/koala-wrapper/src/generated/peers/ForInStatement.ts @@ -16,47 +16,62 @@ import { global, passNode, - passNodeArray, - unpackNonNullableNode, unpackNode, - unpackNodeArray, assertValidPeer, AstNode, Es2pandaAstNodeType, KNativePointer, nodeByType, - ArktsObject, - unpackString -} from "../../reexport-for-generated" +} from '../../reexport-for-generated'; -import { LoopStatement } from "./LoopStatement" -import { Expression } from "./Expression" -import { Statement } from "./Statement" +import { LoopStatement } from './LoopStatement'; +import { Expression } from './Expression'; +import { Statement } from './Statement'; export class ForInStatement extends LoopStatement { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 31) - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_FOR_IN_STATEMENT); + super(pointer); } static createForInStatement(left?: AstNode, right?: Expression, body?: Statement): ForInStatement { - return new ForInStatement(global.generatedEs2panda._CreateForInStatement(global.context, passNode(left), passNode(right), passNode(body))) + return new ForInStatement( + global.generatedEs2panda._CreateForInStatement( + global.context, + passNode(left), + passNode(right), + passNode(body) + ) + ); } - static updateForInStatement(original?: ForInStatement, left?: AstNode, right?: Expression, body?: Statement): ForInStatement { - return new ForInStatement(global.generatedEs2panda._UpdateForInStatement(global.context, passNode(original), passNode(left), passNode(right), passNode(body))) + static updateForInStatement( + original?: ForInStatement, + left?: AstNode, + right?: Expression, + body?: Statement + ): ForInStatement { + return new ForInStatement( + global.generatedEs2panda._UpdateForInStatement( + global.context, + passNode(original), + passNode(left), + passNode(right), + passNode(body) + ) + ); } get left(): AstNode | undefined { - return unpackNode(global.generatedEs2panda._ForInStatementLeftConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForInStatementLeft(global.context, this.peer)); } get right(): Expression | undefined { - return unpackNode(global.generatedEs2panda._ForInStatementRightConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForInStatementRight(global.context, this.peer)); } get body(): Statement | undefined { - return unpackNode(global.generatedEs2panda._ForInStatementBodyConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForInStatementBody(global.context, this.peer)); } + protected readonly brandForInStatement: undefined; } -export function isForInStatement(node: AstNode): node is ForInStatement { - return node instanceof ForInStatement +export function isForInStatement(node: object | undefined): node is ForInStatement { + return node instanceof ForInStatement; +} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_IN_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_IN_STATEMENT, ForInStatement); } -if (!nodeByType.has(31)) { - nodeByType.set(31, ForInStatement) -} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/ForOfStatement.ts b/koala-wrapper/src/generated/peers/ForOfStatement.ts index 801178fd32933fb2b9096c1d09b3372f5845aa13..4ff443a2668991dd4a1ca0e18f7c36da8b4afc43 100644 --- a/koala-wrapper/src/generated/peers/ForOfStatement.ts +++ b/koala-wrapper/src/generated/peers/ForOfStatement.ts @@ -16,50 +16,73 @@ import { global, passNode, - passNodeArray, - unpackNonNullableNode, unpackNode, - unpackNodeArray, assertValidPeer, AstNode, Es2pandaAstNodeType, KNativePointer, nodeByType, - ArktsObject, - unpackString -} from "../../reexport-for-generated" +} from '../../reexport-for-generated'; -import { LoopStatement } from "./LoopStatement" -import { Expression } from "./Expression" -import { Statement } from "./Statement" +import { LoopStatement } from './LoopStatement'; +import { Expression } from './Expression'; +import { Statement } from './Statement'; export class ForOfStatement extends LoopStatement { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 32) - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_FOR_OF_STATEMENT); + super(pointer); } - static createForOfStatement(left: AstNode | undefined, right: Expression | undefined, body: Statement | undefined, isAwait: boolean): ForOfStatement { - return new ForOfStatement(global.generatedEs2panda._CreateForOfStatement(global.context, passNode(left), passNode(right), passNode(body), isAwait)) + static createForOfStatement( + left: AstNode | undefined, + right: Expression | undefined, + body: Statement | undefined, + isAwait: boolean + ): ForOfStatement { + return new ForOfStatement( + global.generatedEs2panda._CreateForOfStatement( + global.context, + passNode(left), + passNode(right), + passNode(body), + isAwait + ) + ); } - static updateForOfStatement(original: ForOfStatement | undefined, left: AstNode | undefined, right: Expression | undefined, body: Statement | undefined, isAwait: boolean): ForOfStatement { - return new ForOfStatement(global.generatedEs2panda._UpdateForOfStatement(global.context, passNode(original), passNode(left), passNode(right), passNode(body), isAwait)) + static updateForOfStatement( + original: ForOfStatement | undefined, + left: AstNode | undefined, + right: Expression | undefined, + body: Statement | undefined, + isAwait: boolean + ): ForOfStatement { + return new ForOfStatement( + global.generatedEs2panda._UpdateForOfStatement( + global.context, + passNode(original), + passNode(left), + passNode(right), + passNode(body), + isAwait + ) + ); } get left(): AstNode | undefined { - return unpackNode(global.generatedEs2panda._ForOfStatementLeftConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForOfStatementLeft(global.context, this.peer)); } get right(): Expression | undefined { - return unpackNode(global.generatedEs2panda._ForOfStatementRightConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForOfStatementRight(global.context, this.peer)); } get body(): Statement | undefined { - return unpackNode(global.generatedEs2panda._ForOfStatementBodyConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForOfStatementBody(global.context, this.peer)); } get isAwait(): boolean { - return global.generatedEs2panda._ForOfStatementIsAwaitConst(global.context, this.peer) + return global.generatedEs2panda._ForOfStatementIsAwaitConst(global.context, this.peer); } + protected readonly brandForOfStatement: undefined; } -export function isForOfStatement(node: AstNode): node is ForOfStatement { - return node instanceof ForOfStatement +export function isForOfStatement(node: object | undefined): node is ForOfStatement { + return node instanceof ForOfStatement; +} +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_OF_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_OF_STATEMENT, ForOfStatement); } -if (!nodeByType.has(32)) { - nodeByType.set(32, ForOfStatement) -} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/ForUpdateStatement.ts b/koala-wrapper/src/generated/peers/ForUpdateStatement.ts index dbf5a96868d171313777d34efb6638a8cbdcf976..ae39482c3d54921122b830f61ef6cab34e120896 100644 --- a/koala-wrapper/src/generated/peers/ForUpdateStatement.ts +++ b/koala-wrapper/src/generated/peers/ForUpdateStatement.ts @@ -16,47 +16,77 @@ import { global, passNode, - passNodeArray, - unpackNonNullableNode, unpackNode, - unpackNodeArray, assertValidPeer, AstNode, - Es2pandaAstNodeType, KNativePointer, nodeByType, - ArktsObject, - unpackString -} from "../../reexport-for-generated" + Es2pandaAstNodeType, +} from '../../reexport-for-generated'; +import { Expression } from './Expression'; +import { LoopStatement } from './LoopStatement'; +import { Statement } from './Statement'; -import { LoopStatement } from "./LoopStatement" -import { Expression } from "./Expression" -import { Statement } from "./Statement" export class ForUpdateStatement extends LoopStatement { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 33) - super(pointer) - + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_FOR_UPDATE_STATEMENT); + super(pointer); } - static createForUpdateStatement(init?: AstNode, test?: Expression, update?: Expression, body?: Statement): ForUpdateStatement { - return new ForUpdateStatement(global.generatedEs2panda._CreateForUpdateStatement(global.context, passNode(init), passNode(test), passNode(update), passNode(body))) + static createForUpdateStatement( + init?: AstNode, + test?: Expression, + update?: Expression, + body?: Statement + ): ForUpdateStatement { + return new ForUpdateStatement( + global.generatedEs2panda._CreateForUpdateStatement( + global.context, + passNode(init), + passNode(test), + passNode(update), + passNode(body) + ) + ); + } + + static updateForUpdateStatement( + original: ForUpdateStatement, + init?: AstNode, + test?: Expression, + update?: Expression, + body?: Statement + ): ForUpdateStatement { + return new ForUpdateStatement( + global.generatedEs2panda._UpdateForUpdateStatement( + global.context, + passNode(original), + passNode(init), + passNode(test), + passNode(update), + passNode(body) + ) + ); } + get init(): AstNode | undefined { - return unpackNode(global.generatedEs2panda._ForUpdateStatementInitConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForUpdateStatementInit(global.context, this.peer)); } get test(): Expression | undefined { - return unpackNode(global.generatedEs2panda._ForUpdateStatementTestConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForUpdateStatementTest(global.context, this.peer)); } get update(): Expression | undefined { - return unpackNode(global.generatedEs2panda._ForUpdateStatementUpdateConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForUpdateStatementUpdateConst(global.context, this.peer)); } get body(): Statement | undefined { - return unpackNode(global.generatedEs2panda._ForUpdateStatementBodyConst(global.context, this.peer)) + return unpackNode(global.generatedEs2panda._ForUpdateStatementBody(global.context, this.peer)); } + protected readonly brandForUpdateStatement: undefined; +} + +export function isForUpdateStatement(node: object | undefined): node is ForUpdateStatement { + return node instanceof ForUpdateStatement; } -export function isForUpdateStatement(node: AstNode): node is ForUpdateStatement { - return node instanceof ForUpdateStatement + +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_UPDATE_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_FOR_UPDATE_STATEMENT, ForUpdateStatement); } -if (!nodeByType.has(33)) { - nodeByType.set(33, ForUpdateStatement) -} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/FunctionDeclaration.ts b/koala-wrapper/src/generated/peers/FunctionDeclaration.ts index 1265a281a578f4a4670b7d15dd3ceadd8aa2191a..598654f02c6e82c0de2a37feeac5eca89f0d3d3e 100644 --- a/koala-wrapper/src/generated/peers/FunctionDeclaration.ts +++ b/koala-wrapper/src/generated/peers/FunctionDeclaration.ts @@ -34,7 +34,7 @@ import { ScriptFunction } from "./ScriptFunction" import { AnnotationUsage } from "./AnnotationUsage" export class FunctionDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 34) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_DECLARATION) super(pointer) } @@ -68,6 +68,6 @@ export class FunctionDeclaration extends Statement { export function isFunctionDeclaration(node: AstNode): node is FunctionDeclaration { return node instanceof FunctionDeclaration } -if (!nodeByType.has(34)) { - nodeByType.set(34, FunctionDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_DECLARATION, FunctionDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/FunctionExpression.ts b/koala-wrapper/src/generated/peers/FunctionExpression.ts index d9e2b3886ad5832e094904b64765155d74b5adae..65c4ee87b9cedb863b6760bfebf33d4bf2c0a784 100644 --- a/koala-wrapper/src/generated/peers/FunctionExpression.ts +++ b/koala-wrapper/src/generated/peers/FunctionExpression.ts @@ -34,7 +34,7 @@ import { ScriptFunction } from "./ScriptFunction" import { Identifier } from "./Identifier" export class FunctionExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 35) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION) super(pointer) } @@ -63,6 +63,6 @@ export class FunctionExpression extends Expression { export function isFunctionExpression(node: AstNode): node is FunctionExpression { return node instanceof FunctionExpression } -if (!nodeByType.has(35)) { - nodeByType.set(35, FunctionExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_FUNCTION_EXPRESSION, FunctionExpression) +} diff --git a/koala-wrapper/src/generated/peers/Identifier.ts b/koala-wrapper/src/generated/peers/Identifier.ts index 042688c12570c36b0fb72c78d26068f8cb19d61b..f6a3967f532e35e8db81c30f48d4bc5df37cb171 100644 --- a/koala-wrapper/src/generated/peers/Identifier.ts +++ b/koala-wrapper/src/generated/peers/Identifier.ts @@ -35,7 +35,7 @@ import { Decorator } from "./Decorator" import { ValidationInfo } from "./ValidationInfo" export class Identifier extends AnnotatedExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 36) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER) super(pointer) } @@ -153,6 +153,6 @@ export class Identifier extends AnnotatedExpression { export function isIdentifier(node: AstNode): node is Identifier { return node instanceof Identifier } -if (!nodeByType.has(36)) { - nodeByType.set(36, Identifier) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, Identifier) +} diff --git a/koala-wrapper/src/generated/peers/IfStatement.ts b/koala-wrapper/src/generated/peers/IfStatement.ts index 056f095f6853e413ec1b83658e3022d6cf385f14..2491ed7c64e6977f005413253beebd70726915e4 100644 --- a/koala-wrapper/src/generated/peers/IfStatement.ts +++ b/koala-wrapper/src/generated/peers/IfStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class IfStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 38) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IF_STATEMENT) super(pointer) } @@ -56,6 +56,6 @@ export class IfStatement extends Statement { export function isIfStatement(node: AstNode): node is IfStatement { return node instanceof IfStatement } -if (!nodeByType.has(38)) { - nodeByType.set(38, IfStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IF_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IF_STATEMENT, IfStatement) +} diff --git a/koala-wrapper/src/generated/peers/ImportDeclaration.ts b/koala-wrapper/src/generated/peers/ImportDeclaration.ts index 3ce4b85d1d6fe48180b5ab0f78c0a9b8889ec3e9..eaed6d45088ab2e34e1503801969bc2e1191715a 100644 --- a/koala-wrapper/src/generated/peers/ImportDeclaration.ts +++ b/koala-wrapper/src/generated/peers/ImportDeclaration.ts @@ -57,6 +57,6 @@ export class ImportDeclaration extends Statement { export function isImportDeclaration(node: AstNode): node is ImportDeclaration { return node instanceof ImportDeclaration } -if (!nodeByType.has(39)) { - nodeByType.set(39, ImportDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DECLARATION, ImportDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/ImportDefaultSpecifier.ts b/koala-wrapper/src/generated/peers/ImportDefaultSpecifier.ts index 5b4ce34e94e9288f145c59de529c6a5d263869ff..2ae1f64be65a37b36d7c48ed9a074fecc0ab73a4 100644 --- a/koala-wrapper/src/generated/peers/ImportDefaultSpecifier.ts +++ b/koala-wrapper/src/generated/peers/ImportDefaultSpecifier.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class ImportDefaultSpecifier extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 41) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER) super(pointer) } @@ -50,6 +50,6 @@ export class ImportDefaultSpecifier extends Statement { export function isImportDefaultSpecifier(node: AstNode): node is ImportDefaultSpecifier { return node instanceof ImportDefaultSpecifier } -if (!nodeByType.has(41)) { - nodeByType.set(41, ImportDefaultSpecifier) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_DEFAULT_SPECIFIER, ImportDefaultSpecifier) +} diff --git a/koala-wrapper/src/generated/peers/ImportExpression.ts b/koala-wrapper/src/generated/peers/ImportExpression.ts index fc70fd1cb4ed843ae5ac46fefc62f10a19693fb3..25ae47eef9ebaab4f9083a6e90877db986115a4b 100644 --- a/koala-wrapper/src/generated/peers/ImportExpression.ts +++ b/koala-wrapper/src/generated/peers/ImportExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class ImportExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 40) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_EXPRESSION) super(pointer) } @@ -49,6 +49,6 @@ export class ImportExpression extends Expression { export function isImportExpression(node: AstNode): node is ImportExpression { return node instanceof ImportExpression } -if (!nodeByType.has(40)) { - nodeByType.set(40, ImportExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_EXPRESSION, ImportExpression) +} diff --git a/koala-wrapper/src/generated/peers/ImportNamespaceSpecifier.ts b/koala-wrapper/src/generated/peers/ImportNamespaceSpecifier.ts index 4f1e128bf7b2036f5798b15c2a8271cd7323c8dc..4caa2e2e8112f2632af7c10b6ae68a47814c010b 100644 --- a/koala-wrapper/src/generated/peers/ImportNamespaceSpecifier.ts +++ b/koala-wrapper/src/generated/peers/ImportNamespaceSpecifier.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class ImportNamespaceSpecifier extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 42) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER) super(pointer) } @@ -50,6 +50,6 @@ export class ImportNamespaceSpecifier extends Statement { export function isImportNamespaceSpecifier(node: AstNode): node is ImportNamespaceSpecifier { return node instanceof ImportNamespaceSpecifier } -if (!nodeByType.has(42)) { - nodeByType.set(42, ImportNamespaceSpecifier) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_NAMESPACE_SPECIFIER, ImportNamespaceSpecifier) +} diff --git a/koala-wrapper/src/generated/peers/ImportSpecifier.ts b/koala-wrapper/src/generated/peers/ImportSpecifier.ts index e2d49f13954c6894021f28dacc4133124674c028..828f1eebf795438fe82458aad57906870a462e59 100644 --- a/koala-wrapper/src/generated/peers/ImportSpecifier.ts +++ b/koala-wrapper/src/generated/peers/ImportSpecifier.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class ImportSpecifier extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 43) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_SPECIFIER) super(pointer) } @@ -53,6 +53,6 @@ export class ImportSpecifier extends Statement { export function isImportSpecifier(node: AstNode): node is ImportSpecifier { return node instanceof ImportSpecifier } -if (!nodeByType.has(43)) { - nodeByType.set(43, ImportSpecifier) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_SPECIFIER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_IMPORT_SPECIFIER, ImportSpecifier) +} diff --git a/koala-wrapper/src/generated/peers/LabelPair.ts b/koala-wrapper/src/generated/peers/LabelPair.ts new file mode 100644 index 0000000000000000000000000000000000000000..c949e6fc1e8803594a320aaf5b33c8f946c4975e --- /dev/null +++ b/koala-wrapper/src/generated/peers/LabelPair.ts @@ -0,0 +1,35 @@ +/* + * 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 { + global, + passNode, + passNodeArray, + unpackNonNullableNode, + unpackNode, + unpackNodeArray, + assertValidPeer, + AstNode, + KNativePointer, + nodeByType, + ArktsObject, + unpackString +} from "../../reexport-for-generated" + +export class LabelPair extends AstNode { + constructor(pointer: KNativePointer) { + super(pointer) + } +} \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/LabelledStatement.ts b/koala-wrapper/src/generated/peers/LabelledStatement.ts index 7dec2f9eefc2a39e743c7cafd6c1d2303131b71e..9b9ac14b184f1cb6d032ec2d72d0ea3f45200bfe 100644 --- a/koala-wrapper/src/generated/peers/LabelledStatement.ts +++ b/koala-wrapper/src/generated/peers/LabelledStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Identifier } from "./Identifier" export class LabelledStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 44) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_LABELLED_STATEMENT) super(pointer) } @@ -53,6 +53,6 @@ export class LabelledStatement extends Statement { export function isLabelledStatement(node: AstNode): node is LabelledStatement { return node instanceof LabelledStatement } -if (!nodeByType.has(44)) { - nodeByType.set(44, LabelledStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_LABELLED_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_LABELLED_STATEMENT, LabelledStatement) +} diff --git a/koala-wrapper/src/generated/peers/MemberExpression.ts b/koala-wrapper/src/generated/peers/MemberExpression.ts index 40f74fe00d23081055257ea89936c5a6b138befc..530fc754eec9d116f80a260864e2c26da9766b7f 100644 --- a/koala-wrapper/src/generated/peers/MemberExpression.ts +++ b/koala-wrapper/src/generated/peers/MemberExpression.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { Es2pandaMemberExpressionKind } from "./../Es2pandaEnums" export class MemberExpression extends MaybeOptionalExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 45) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION) super(pointer) } @@ -88,6 +88,6 @@ export class MemberExpression extends MaybeOptionalExpression { export function isMemberExpression(node: AstNode): node is MemberExpression { return node instanceof MemberExpression } -if (!nodeByType.has(45)) { - nodeByType.set(45, MemberExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION, MemberExpression) +} diff --git a/koala-wrapper/src/generated/peers/MetaProperty.ts b/koala-wrapper/src/generated/peers/MetaProperty.ts index 82c8bc8aefe4b95e1cb50d62557224e2c278221e..fbdb77b242659fc53741af8cae7f1cbed4be4841 100644 --- a/koala-wrapper/src/generated/peers/MetaProperty.ts +++ b/koala-wrapper/src/generated/peers/MetaProperty.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Es2pandaMetaPropertyKind } from "./../Es2pandaEnums" export class MetaProperty extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 46) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_META_PROPERTY_EXPRESSION) super(pointer) } @@ -50,6 +50,6 @@ export class MetaProperty extends Expression { export function isMetaProperty(node: AstNode): node is MetaProperty { return node instanceof MetaProperty } -if (!nodeByType.has(46)) { - nodeByType.set(46, MetaProperty) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_META_PROPERTY_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_META_PROPERTY_EXPRESSION, MetaProperty) +} diff --git a/koala-wrapper/src/generated/peers/MethodDefinition.ts b/koala-wrapper/src/generated/peers/MethodDefinition.ts index 40dd138885e53cb27b27c8790cab2b376b929386..0cfbcff2356acda559e9295d501686625f17415c 100644 --- a/koala-wrapper/src/generated/peers/MethodDefinition.ts +++ b/koala-wrapper/src/generated/peers/MethodDefinition.ts @@ -36,7 +36,7 @@ import { Es2pandaModifierFlags } from "./../Es2pandaEnums" import { ScriptFunction } from "./ScriptFunction" export class MethodDefinition extends ClassElement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 47) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION) super(pointer) } @@ -93,6 +93,6 @@ export class MethodDefinition extends ClassElement { export function isMethodDefinition(node: AstNode): node is MethodDefinition { return node instanceof MethodDefinition } -if (!nodeByType.has(47)) { - nodeByType.set(47, MethodDefinition) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, MethodDefinition) +} diff --git a/koala-wrapper/src/generated/peers/NamedType.ts b/koala-wrapper/src/generated/peers/NamedType.ts index ce6ee368ecb13dedda9f0af71bbeabe19069ac89..a95328543c965ced59164c22a57c77ed29224138 100644 --- a/koala-wrapper/src/generated/peers/NamedType.ts +++ b/koala-wrapper/src/generated/peers/NamedType.ts @@ -34,7 +34,7 @@ import { Identifier } from "./Identifier" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class NamedType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 48) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_NAMED_TYPE) super(pointer) } @@ -72,6 +72,6 @@ export class NamedType extends TypeNode { export function isNamedType(node: AstNode): node is NamedType { return node instanceof NamedType } -if (!nodeByType.has(48)) { - nodeByType.set(48, NamedType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_NAMED_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_NAMED_TYPE, NamedType) +} diff --git a/koala-wrapper/src/generated/peers/NewExpression.ts b/koala-wrapper/src/generated/peers/NewExpression.ts index c267a581dbae85d8c497c7d0c52e6856478a9c2a..f09cf3d6ee1d5d6bc745d45b65afc5fd5e537565 100644 --- a/koala-wrapper/src/generated/peers/NewExpression.ts +++ b/koala-wrapper/src/generated/peers/NewExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class NewExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 49) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_NEW_EXPRESSION) super(pointer) } @@ -52,6 +52,6 @@ export class NewExpression extends Expression { export function isNewExpression(node: AstNode): node is NewExpression { return node instanceof NewExpression } -if (!nodeByType.has(49)) { - nodeByType.set(49, NewExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_NEW_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_NEW_EXPRESSION, NewExpression) +} diff --git a/koala-wrapper/src/generated/peers/NullLiteral.ts b/koala-wrapper/src/generated/peers/NullLiteral.ts index a8047b711ba2a9f2c70cd04e36e7474a5fb1e08b..11f7510216548a926ab502ab7c795be4c7ad2d36 100644 --- a/koala-wrapper/src/generated/peers/NullLiteral.ts +++ b/koala-wrapper/src/generated/peers/NullLiteral.ts @@ -32,7 +32,7 @@ import { import { Literal } from "./Literal" export class NullLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 50) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL) super(pointer) } @@ -46,6 +46,6 @@ export class NullLiteral extends Literal { export function isNullLiteral(node: AstNode): node is NullLiteral { return node instanceof NullLiteral } -if (!nodeByType.has(50)) { - nodeByType.set(50, NullLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_NULL_LITERAL, NullLiteral) +} diff --git a/koala-wrapper/src/generated/peers/NumberLiteral.ts b/koala-wrapper/src/generated/peers/NumberLiteral.ts index e14e174d9b49bdfbdf91958cb8727724dba60478..0930bbc0275ee4fbf5e2bdf350750adc4015a36d 100644 --- a/koala-wrapper/src/generated/peers/NumberLiteral.ts +++ b/koala-wrapper/src/generated/peers/NumberLiteral.ts @@ -32,17 +32,14 @@ import { import { Literal } from "./Literal" export class NumberLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 52) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL) super(pointer) } - get str(): string { - return unpackString(global.generatedEs2panda._NumberLiteralStrConst(global.context, this.peer)) - } } export function isNumberLiteral(node: AstNode): node is NumberLiteral { return node instanceof NumberLiteral } -if (!nodeByType.has(52)) { - nodeByType.set(52, NumberLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_NUMBER_LITERAL, NumberLiteral) +} diff --git a/koala-wrapper/src/generated/peers/OmittedExpression.ts b/koala-wrapper/src/generated/peers/OmittedExpression.ts index 339202ae39a669ba597317c4e507141a5a6e2281..4449960a1c75dade754f31c768ab09b9b85a991d 100644 --- a/koala-wrapper/src/generated/peers/OmittedExpression.ts +++ b/koala-wrapper/src/generated/peers/OmittedExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class OmittedExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 53) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_OMITTED_EXPRESSION) super(pointer) } @@ -46,6 +46,6 @@ export class OmittedExpression extends Expression { export function isOmittedExpression(node: AstNode): node is OmittedExpression { return node instanceof OmittedExpression } -if (!nodeByType.has(53)) { - nodeByType.set(53, OmittedExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_OMITTED_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_OMITTED_EXPRESSION, OmittedExpression) +} diff --git a/koala-wrapper/src/generated/peers/OpaqueTypeNode.ts b/koala-wrapper/src/generated/peers/OpaqueTypeNode.ts index ba2d646dc76d75a1a471ad9657da6676f42a3454..8fbc0101b3724686117eaff15ebeb32a32ae1510 100644 --- a/koala-wrapper/src/generated/peers/OpaqueTypeNode.ts +++ b/koala-wrapper/src/generated/peers/OpaqueTypeNode.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class OpaqueTypeNode extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 154) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_OPAQUE_TYPE_NODE) super(pointer) } @@ -46,6 +46,6 @@ export class OpaqueTypeNode extends TypeNode { export function isOpaqueTypeNode(node: AstNode): node is OpaqueTypeNode { return node instanceof OpaqueTypeNode } -if (!nodeByType.has(154)) { - nodeByType.set(154, OpaqueTypeNode) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_OPAQUE_TYPE_NODE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_OPAQUE_TYPE_NODE, OpaqueTypeNode) +} diff --git a/koala-wrapper/src/generated/peers/PrefixAssertionExpression.ts b/koala-wrapper/src/generated/peers/PrefixAssertionExpression.ts index 03a093531ad7fbc973070172e35bde721d484ba4..30dc3d43af3910da23146749eadc45d6de6c4069 100644 --- a/koala-wrapper/src/generated/peers/PrefixAssertionExpression.ts +++ b/koala-wrapper/src/generated/peers/PrefixAssertionExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class PrefixAssertionExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 54) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION) super(pointer) } @@ -53,6 +53,6 @@ export class PrefixAssertionExpression extends Expression { export function isPrefixAssertionExpression(node: AstNode): node is PrefixAssertionExpression { return node instanceof PrefixAssertionExpression } -if (!nodeByType.has(54)) { - nodeByType.set(54, PrefixAssertionExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_PREFIX_ASSERTION_EXPRESSION, PrefixAssertionExpression) +} diff --git a/koala-wrapper/src/generated/peers/Property.ts b/koala-wrapper/src/generated/peers/Property.ts index 982cb02413ecbeaf600ddf7b78e8b54056f9004b..6a576762b68ffb15f132e266237c7eb50f854c2f 100644 --- a/koala-wrapper/src/generated/peers/Property.ts +++ b/koala-wrapper/src/generated/peers/Property.ts @@ -34,7 +34,7 @@ import { Es2pandaPropertyKind } from "./../Es2pandaEnums" import { ValidationInfo } from "./ValidationInfo" export class Property extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 55) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY) super(pointer) } @@ -75,6 +75,6 @@ export class Property extends Expression { export function isProperty(node: AstNode): node is Property { return node instanceof Property } -if (!nodeByType.has(55)) { - nodeByType.set(55, Property) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, Property) +} diff --git a/koala-wrapper/src/generated/peers/RegExpLiteral.ts b/koala-wrapper/src/generated/peers/RegExpLiteral.ts index 5ae04143bb60b4d42789e0a61783356d99c83151..0d91a57d9425c927779bb96723dcf007723ff097 100644 --- a/koala-wrapper/src/generated/peers/RegExpLiteral.ts +++ b/koala-wrapper/src/generated/peers/RegExpLiteral.ts @@ -33,7 +33,7 @@ import { Literal } from "./Literal" import { Es2pandaRegExpFlags } from "./../Es2pandaEnums" export class RegExpLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 56) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_REGEXP_LITERAL) super(pointer) } @@ -53,6 +53,6 @@ export class RegExpLiteral extends Literal { export function isRegExpLiteral(node: AstNode): node is RegExpLiteral { return node instanceof RegExpLiteral } -if (!nodeByType.has(56)) { - nodeByType.set(56, RegExpLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_REGEXP_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_REGEXP_LITERAL, RegExpLiteral) +} diff --git a/koala-wrapper/src/generated/peers/ReturnStatement.ts b/koala-wrapper/src/generated/peers/ReturnStatement.ts index 6489e99dfb318500e52b7db8e54ae6111c59ffab..4607dbe406b060bbd511262b7bf074718615d20b 100644 --- a/koala-wrapper/src/generated/peers/ReturnStatement.ts +++ b/koala-wrapper/src/generated/peers/ReturnStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class ReturnStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 58) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT) super(pointer) } @@ -61,6 +61,6 @@ export class ReturnStatement extends Statement { export function isReturnStatement(node: AstNode): node is ReturnStatement { return node instanceof ReturnStatement } -if (!nodeByType.has(58)) { - nodeByType.set(58, ReturnStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, ReturnStatement) +} diff --git a/koala-wrapper/src/generated/peers/ScriptFunction.ts b/koala-wrapper/src/generated/peers/ScriptFunction.ts index 28b262008fcc242eee9b1c1806cb945a2bad5a5e..d04c69b5e0d0d3cb32eea3f5fa996c840e9944da 100644 --- a/koala-wrapper/src/generated/peers/ScriptFunction.ts +++ b/koala-wrapper/src/generated/peers/ScriptFunction.ts @@ -40,7 +40,7 @@ import { Es2pandaModifierFlags } from "./../Es2pandaEnums" import { AnnotationUsage } from "./AnnotationUsage" export class ScriptFunction extends AstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 59) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION) super(pointer) } @@ -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[] { @@ -194,6 +188,6 @@ export class ScriptFunction extends AstNode { export function isScriptFunction(node: AstNode): node is ScriptFunction { return node instanceof ScriptFunction } -if (!nodeByType.has(59)) { - nodeByType.set(59, ScriptFunction) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, ScriptFunction) +} diff --git a/koala-wrapper/src/generated/peers/SequenceExpression.ts b/koala-wrapper/src/generated/peers/SequenceExpression.ts index fb0f1a9c0e26a92d0495b37606ee3a8e88b5dc5b..dc814dd4d3cc16c9eea3ed65bed61ccf23255672 100644 --- a/koala-wrapper/src/generated/peers/SequenceExpression.ts +++ b/koala-wrapper/src/generated/peers/SequenceExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class SequenceExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 60) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SEQUENCE_EXPRESSION) super(pointer) } @@ -49,6 +49,6 @@ export class SequenceExpression extends Expression { export function isSequenceExpression(node: AstNode): node is SequenceExpression { return node instanceof SequenceExpression } -if (!nodeByType.has(60)) { - nodeByType.set(60, SequenceExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SEQUENCE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SEQUENCE_EXPRESSION, SequenceExpression) +} diff --git a/koala-wrapper/src/generated/peers/StringLiteral.ts b/koala-wrapper/src/generated/peers/StringLiteral.ts index 6eed3c9e44c8be71cdf92977865bd65786df2713..cf03dca5c79d2c37368c0c0874399b1c3d5d19db 100644 --- a/koala-wrapper/src/generated/peers/StringLiteral.ts +++ b/koala-wrapper/src/generated/peers/StringLiteral.ts @@ -31,7 +31,7 @@ import { import { Literal } from "./Literal" export class StringLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 61) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_STRING_LITERAL) super(pointer) } @@ -54,6 +54,6 @@ export class StringLiteral extends Literal { export function isStringLiteral(node: AstNode): node is StringLiteral { return node instanceof StringLiteral } -if (!nodeByType.has(61)) { - nodeByType.set(61, StringLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_STRING_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_STRING_LITERAL, StringLiteral) +} diff --git a/koala-wrapper/src/generated/peers/SuperExpression.ts b/koala-wrapper/src/generated/peers/SuperExpression.ts index 57b35074d90858cad02086784e6bedc0ccd4511a..6f7c877c01bf952c899a176be2cfd03cbf49fa42 100644 --- a/koala-wrapper/src/generated/peers/SuperExpression.ts +++ b/koala-wrapper/src/generated/peers/SuperExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class SuperExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 83) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION) super(pointer) } @@ -46,6 +46,6 @@ export class SuperExpression extends Expression { export function isSuperExpression(node: AstNode): node is SuperExpression { return node instanceof SuperExpression } -if (!nodeByType.has(83)) { - nodeByType.set(83, SuperExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION, SuperExpression) +} diff --git a/koala-wrapper/src/generated/peers/SwitchCaseStatement.ts b/koala-wrapper/src/generated/peers/SwitchCaseStatement.ts index 93ff25f0828d253d01e18ca96c88a73d69e4d3f2..27f1d7e98ad3fbb9e54bfbe747e58cdb147ae76e 100644 --- a/koala-wrapper/src/generated/peers/SwitchCaseStatement.ts +++ b/koala-wrapper/src/generated/peers/SwitchCaseStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class SwitchCaseStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 85) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_CASE_STATEMENT) super(pointer) } @@ -53,6 +53,6 @@ export class SwitchCaseStatement extends Statement { export function isSwitchCaseStatement(node: AstNode): node is SwitchCaseStatement { return node instanceof SwitchCaseStatement } -if (!nodeByType.has(85)) { - nodeByType.set(85, SwitchCaseStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_CASE_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_CASE_STATEMENT, SwitchCaseStatement) +} diff --git a/koala-wrapper/src/generated/peers/SwitchStatement.ts b/koala-wrapper/src/generated/peers/SwitchStatement.ts index d11afaf4a48d91397a7cc629b38e505db28fcc2a..d7c9289cddb6d2c896e7f5a634bb7b87e2ed4aa6 100644 --- a/koala-wrapper/src/generated/peers/SwitchStatement.ts +++ b/koala-wrapper/src/generated/peers/SwitchStatement.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { SwitchCaseStatement } from "./SwitchCaseStatement" export class SwitchStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 86) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_STATEMENT) super(pointer) } @@ -54,6 +54,6 @@ export class SwitchStatement extends Statement { export function isSwitchStatement(node: AstNode): node is SwitchStatement { return node instanceof SwitchStatement } -if (!nodeByType.has(86)) { - nodeByType.set(86, SwitchStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_SWITCH_STATEMENT, SwitchStatement) +} diff --git a/koala-wrapper/src/generated/peers/TSAnyKeyword.ts b/koala-wrapper/src/generated/peers/TSAnyKeyword.ts index ffb19bebf574a08dfd9ffa2d5ce1a2fb6842551e..7f0133efbdf3f8a0ac682e44e7c515955486a958 100644 --- a/koala-wrapper/src/generated/peers/TSAnyKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSAnyKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSAnyKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 91) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_ANY_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSAnyKeyword extends TypeNode { export function isTSAnyKeyword(node: AstNode): node is TSAnyKeyword { return node instanceof TSAnyKeyword } -if (!nodeByType.has(91)) { - nodeByType.set(91, TSAnyKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ANY_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ANY_KEYWORD, TSAnyKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSArrayType.ts b/koala-wrapper/src/generated/peers/TSArrayType.ts index a727d24fc5dca3238d288a39b11e1a6b1c07c5fa..475b248cc74942f8b8805a8b16183fb4c99e9f5c 100644 --- a/koala-wrapper/src/generated/peers/TSArrayType.ts +++ b/koala-wrapper/src/generated/peers/TSArrayType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSArrayType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 102) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_ARRAY_TYPE) super(pointer) } @@ -49,6 +49,6 @@ export class TSArrayType extends TypeNode { export function isTSArrayType(node: AstNode): node is TSArrayType { return node instanceof TSArrayType } -if (!nodeByType.has(102)) { - nodeByType.set(102, TSArrayType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ARRAY_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ARRAY_TYPE, TSArrayType) +} diff --git a/koala-wrapper/src/generated/peers/TSAsExpression.ts b/koala-wrapper/src/generated/peers/TSAsExpression.ts index 718b9427b9cad33a63ec2805521998160959c1ed..99c58edd4b378edf5b2718778ce707de5bf8e73e 100644 --- a/koala-wrapper/src/generated/peers/TSAsExpression.ts +++ b/koala-wrapper/src/generated/peers/TSAsExpression.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class TSAsExpression extends AnnotatedExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 138) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_AS_EXPRESSION) super(pointer) } @@ -72,6 +72,6 @@ export class TSAsExpression extends AnnotatedExpression { export function isTSAsExpression(node: AstNode): node is TSAsExpression { return node instanceof TSAsExpression } -if (!nodeByType.has(138)) { - nodeByType.set(138, TSAsExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_AS_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_AS_EXPRESSION, TSAsExpression) +} diff --git a/koala-wrapper/src/generated/peers/TSBigintKeyword.ts b/koala-wrapper/src/generated/peers/TSBigintKeyword.ts index 371719a14d15a89cb838aa52b41a594ac9e65c0f..12da1be0697da8bacfcf64384a56b75e248553ad 100644 --- a/koala-wrapper/src/generated/peers/TSBigintKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSBigintKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSBigintKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 98) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_BIGINT_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSBigintKeyword extends TypeNode { export function isTSBigintKeyword(node: AstNode): node is TSBigintKeyword { return node instanceof TSBigintKeyword } -if (!nodeByType.has(98)) { - nodeByType.set(98, TSBigintKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_BIGINT_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_BIGINT_KEYWORD, TSBigintKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSBooleanKeyword.ts b/koala-wrapper/src/generated/peers/TSBooleanKeyword.ts index cc4c077b38ec11d5ba97506d0916dac29b30555e..6459f6fdb33dcc965f969ed4ad81cdb38561c93e 100644 --- a/koala-wrapper/src/generated/peers/TSBooleanKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSBooleanKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSBooleanKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 93) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_BOOLEAN_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSBooleanKeyword extends TypeNode { export function isTSBooleanKeyword(node: AstNode): node is TSBooleanKeyword { return node instanceof TSBooleanKeyword } -if (!nodeByType.has(93)) { - nodeByType.set(93, TSBooleanKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_BOOLEAN_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_BOOLEAN_KEYWORD, TSBooleanKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSClassImplements.ts b/koala-wrapper/src/generated/peers/TSClassImplements.ts index b06a578a14da675e39cdac734f41e74dd81508e3..ed621c26068322fd58c2caeb53b4c4f36ceb97a2 100644 --- a/koala-wrapper/src/generated/peers/TSClassImplements.ts +++ b/koala-wrapper/src/generated/peers/TSClassImplements.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class TSClassImplements extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 139) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_CLASS_IMPLEMENTS) super(pointer) } @@ -59,6 +59,6 @@ export class TSClassImplements extends Expression { export function isTSClassImplements(node: AstNode): node is TSClassImplements { return node instanceof TSClassImplements } -if (!nodeByType.has(139)) { - nodeByType.set(139, TSClassImplements) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CLASS_IMPLEMENTS)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CLASS_IMPLEMENTS, TSClassImplements) +} diff --git a/koala-wrapper/src/generated/peers/TSConditionalType.ts b/koala-wrapper/src/generated/peers/TSConditionalType.ts index f03de860013dad592909b90fad5130a967d21b1b..6b6ab8f22ce26bf381cc6d2738542e9a1ecf1397 100644 --- a/koala-wrapper/src/generated/peers/TSConditionalType.ts +++ b/koala-wrapper/src/generated/peers/TSConditionalType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSConditionalType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 111) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONDITIONAL_TYPE) super(pointer) } @@ -59,6 +59,6 @@ export class TSConditionalType extends TypeNode { export function isTSConditionalType(node: AstNode): node is TSConditionalType { return node instanceof TSConditionalType } -if (!nodeByType.has(111)) { - nodeByType.set(111, TSConditionalType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONDITIONAL_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONDITIONAL_TYPE, TSConditionalType) +} diff --git a/koala-wrapper/src/generated/peers/TSConstructorType.ts b/koala-wrapper/src/generated/peers/TSConstructorType.ts index 896ccf390be4d298b823c61137863f04c05f88aa..be2be772c86b1443ff45c7497943a98ac4efea33 100644 --- a/koala-wrapper/src/generated/peers/TSConstructorType.ts +++ b/koala-wrapper/src/generated/peers/TSConstructorType.ts @@ -35,7 +35,7 @@ import { TSTypeParameterDeclaration } from "./TSTypeParameterDeclaration" import { Expression } from "./Expression" export class TSConstructorType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 126) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE) super(pointer) } @@ -61,6 +61,6 @@ export class TSConstructorType extends TypeNode { export function isTSConstructorType(node: AstNode): node is TSConstructorType { return node instanceof TSConstructorType } -if (!nodeByType.has(126)) { - nodeByType.set(126, TSConstructorType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_CONSTRUCTOR_TYPE, TSConstructorType) +} diff --git a/koala-wrapper/src/generated/peers/TSEnumDeclaration.ts b/koala-wrapper/src/generated/peers/TSEnumDeclaration.ts index ecebdceedaf98864bb9b8a72347ff1f3a38aefc4..707334a8cfb265869eb437386adae5f930d2e95f 100644 --- a/koala-wrapper/src/generated/peers/TSEnumDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSEnumDeclaration.ts @@ -35,7 +35,7 @@ import { ClassDefinition } from "./ClassDefinition" import { Decorator } from "./Decorator" export class TSEnumDeclaration extends TypedStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 87) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_DECLARATION) super(pointer) } @@ -77,6 +77,6 @@ export class TSEnumDeclaration extends TypedStatement { export function isTSEnumDeclaration(node: AstNode): node is TSEnumDeclaration { return node instanceof TSEnumDeclaration } -if (!nodeByType.has(87)) { - nodeByType.set(87, TSEnumDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_DECLARATION, TSEnumDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSEnumMember.ts b/koala-wrapper/src/generated/peers/TSEnumMember.ts index c0332c50959237dc14bb86c42cf4169aa46083e4..862e088671d33bc054f3f6d9398331a37dfe3e3f 100644 --- a/koala-wrapper/src/generated/peers/TSEnumMember.ts +++ b/koala-wrapper/src/generated/peers/TSEnumMember.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class TSEnumMember extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 88) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_MEMBER) super(pointer) } @@ -62,6 +62,6 @@ export class TSEnumMember extends Statement { export function isTSEnumMember(node: AstNode): node is TSEnumMember { return node instanceof TSEnumMember } -if (!nodeByType.has(88)) { - nodeByType.set(88, TSEnumMember) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_MEMBER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_ENUM_MEMBER, TSEnumMember) +} diff --git a/koala-wrapper/src/generated/peers/TSExternalModuleReference.ts b/koala-wrapper/src/generated/peers/TSExternalModuleReference.ts index eae8cfabc8bd2ecfafb0b9419ae24925fcad2540..44303a741f42b95d85c62a642f6dc5aa8da993a2 100644 --- a/koala-wrapper/src/generated/peers/TSExternalModuleReference.ts +++ b/koala-wrapper/src/generated/peers/TSExternalModuleReference.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class TSExternalModuleReference extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 89) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE) super(pointer) } @@ -49,6 +49,6 @@ export class TSExternalModuleReference extends Expression { export function isTSExternalModuleReference(node: AstNode): node is TSExternalModuleReference { return node instanceof TSExternalModuleReference } -if (!nodeByType.has(89)) { - nodeByType.set(89, TSExternalModuleReference) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_EXTERNAL_MODULE_REFERENCE, TSExternalModuleReference) +} diff --git a/koala-wrapper/src/generated/peers/TSFunctionType.ts b/koala-wrapper/src/generated/peers/TSFunctionType.ts index f65d2fa566d8eb7e4d1cdf70b583ec8ef9ad618f..6f2df8cb4d06e5e4527597505d7d512dac771ad1 100644 --- a/koala-wrapper/src/generated/peers/TSFunctionType.ts +++ b/koala-wrapper/src/generated/peers/TSFunctionType.ts @@ -35,7 +35,7 @@ import { TSTypeParameterDeclaration } from "./TSTypeParameterDeclaration" import { Expression } from "./Expression" export class TSFunctionType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 125) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_FUNCTION_TYPE) super(pointer) } @@ -63,6 +63,6 @@ export class TSFunctionType extends TypeNode { export function isTSFunctionType(node: AstNode): node is TSFunctionType { return node instanceof TSFunctionType } -if (!nodeByType.has(125)) { - nodeByType.set(125, TSFunctionType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_FUNCTION_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_FUNCTION_TYPE, TSFunctionType) +} diff --git a/koala-wrapper/src/generated/peers/TSImportEqualsDeclaration.ts b/koala-wrapper/src/generated/peers/TSImportEqualsDeclaration.ts index a04b883796522a8a313d448e9e5e7e6c08023c55..6f561a769134bb751fed615aeabf37df6d370e36 100644 --- a/koala-wrapper/src/generated/peers/TSImportEqualsDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSImportEqualsDeclaration.ts @@ -34,7 +34,7 @@ import { Identifier } from "./Identifier" import { Expression } from "./Expression" export class TSImportEqualsDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 124) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION) super(pointer) } @@ -57,6 +57,6 @@ export class TSImportEqualsDeclaration extends Statement { export function isTSImportEqualsDeclaration(node: AstNode): node is TSImportEqualsDeclaration { return node instanceof TSImportEqualsDeclaration } -if (!nodeByType.has(124)) { - nodeByType.set(124, TSImportEqualsDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_EQUALS_DECLARATION, TSImportEqualsDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSImportType.ts b/koala-wrapper/src/generated/peers/TSImportType.ts index 43e08e49a1c30ae25600c24a4aa10d1ebbf17d35..7f57afe79c2cf80db73b8aec4d36cf793b3872d0 100644 --- a/koala-wrapper/src/generated/peers/TSImportType.ts +++ b/koala-wrapper/src/generated/peers/TSImportType.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class TSImportType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 112) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_TYPE) super(pointer) } @@ -60,6 +60,6 @@ export class TSImportType extends TypeNode { export function isTSImportType(node: AstNode): node is TSImportType { return node instanceof TSImportType } -if (!nodeByType.has(112)) { - nodeByType.set(112, TSImportType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_IMPORT_TYPE, TSImportType) +} diff --git a/koala-wrapper/src/generated/peers/TSIndexSignature.ts b/koala-wrapper/src/generated/peers/TSIndexSignature.ts index 6a7bf5ea97ef09456502e9ebd415fe656df67b4c..827218b2535a18b8466a013fbf6edb75915403d5 100644 --- a/koala-wrapper/src/generated/peers/TSIndexSignature.ts +++ b/koala-wrapper/src/generated/peers/TSIndexSignature.ts @@ -35,7 +35,7 @@ import { TypeNode } from "./TypeNode" import { Es2pandaTSIndexSignatureKind } from "./../Es2pandaEnums" export class TSIndexSignature extends TypedAstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 136) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEX_SIGNATURE) super(pointer) } @@ -58,6 +58,6 @@ export class TSIndexSignature extends TypedAstNode { export function isTSIndexSignature(node: AstNode): node is TSIndexSignature { return node instanceof TSIndexSignature } -if (!nodeByType.has(136)) { - nodeByType.set(136, TSIndexSignature) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEX_SIGNATURE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEX_SIGNATURE, TSIndexSignature) +} diff --git a/koala-wrapper/src/generated/peers/TSIndexedAccessType.ts b/koala-wrapper/src/generated/peers/TSIndexedAccessType.ts index edb3a5c1f6365c7674a3693e6fb849033ea0e4aa..7faab853a6e1ae76e88ec77a045c6ed481a424ed 100644 --- a/koala-wrapper/src/generated/peers/TSIndexedAccessType.ts +++ b/koala-wrapper/src/generated/peers/TSIndexedAccessType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSIndexedAccessType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 130) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE) super(pointer) } @@ -52,6 +52,6 @@ export class TSIndexedAccessType extends TypeNode { export function isTSIndexedAccessType(node: AstNode): node is TSIndexedAccessType { return node instanceof TSIndexedAccessType } -if (!nodeByType.has(130)) { - nodeByType.set(130, TSIndexedAccessType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INDEXED_ACCESS_TYPE, TSIndexedAccessType) +} diff --git a/koala-wrapper/src/generated/peers/TSInferType.ts b/koala-wrapper/src/generated/peers/TSInferType.ts index f2a5d65978b6fe1f92a2fa113f9ad75f84c318cf..aea447732553786956a62ba9d694692a2df75443 100644 --- a/koala-wrapper/src/generated/peers/TSInferType.ts +++ b/koala-wrapper/src/generated/peers/TSInferType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { TSTypeParameter } from "./TSTypeParameter" export class TSInferType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 110) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INFER_TYPE) super(pointer) } @@ -50,6 +50,6 @@ export class TSInferType extends TypeNode { export function isTSInferType(node: AstNode): node is TSInferType { return node instanceof TSInferType } -if (!nodeByType.has(110)) { - nodeByType.set(110, TSInferType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INFER_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INFER_TYPE, TSInferType) +} diff --git a/koala-wrapper/src/generated/peers/TSInterfaceBody.ts b/koala-wrapper/src/generated/peers/TSInterfaceBody.ts index 103fa4a1aec9c1a8d5158a9e228e67d1150faa18..fa9c4e80a25e5c947f2abcce20fbba866aea4fc3 100644 --- a/koala-wrapper/src/generated/peers/TSInterfaceBody.ts +++ b/koala-wrapper/src/generated/peers/TSInterfaceBody.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class TSInterfaceBody extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 132) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_BODY) super(pointer) } @@ -52,6 +52,6 @@ export class TSInterfaceBody extends Expression { export function isTSInterfaceBody(node: AstNode): node is TSInterfaceBody { return node instanceof TSInterfaceBody } -if (!nodeByType.has(132)) { - nodeByType.set(132, TSInterfaceBody) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_BODY)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_BODY, TSInterfaceBody) +} diff --git a/koala-wrapper/src/generated/peers/TSInterfaceDeclaration.ts b/koala-wrapper/src/generated/peers/TSInterfaceDeclaration.ts index 21c064fcf2b0167f0526b1b68767a2c2c2c6ad8c..a44a0c8e6fde96c9848732011a77b0517ebacdc5 100644 --- a/koala-wrapper/src/generated/peers/TSInterfaceDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSInterfaceDeclaration.ts @@ -39,7 +39,7 @@ import { ClassDeclaration } from "./ClassDeclaration" import { AnnotationUsage } from "./AnnotationUsage" export class TSInterfaceDeclaration extends TypedStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 131) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_DECLARATION) super(pointer) } @@ -98,6 +98,6 @@ export class TSInterfaceDeclaration extends TypedStatement { export function isTSInterfaceDeclaration(node: AstNode): node is TSInterfaceDeclaration { return node instanceof TSInterfaceDeclaration } -if (!nodeByType.has(131)) { - nodeByType.set(131, TSInterfaceDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_DECLARATION, TSInterfaceDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSInterfaceHeritage.ts b/koala-wrapper/src/generated/peers/TSInterfaceHeritage.ts index 4af6bfb0fae36e26370b5a601e170588462d80cd..4b22c6eaa6a85230ba49490cb752ff86fb6ce28b 100644 --- a/koala-wrapper/src/generated/peers/TSInterfaceHeritage.ts +++ b/koala-wrapper/src/generated/peers/TSInterfaceHeritage.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class TSInterfaceHeritage extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 133) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_HERITAGE) super(pointer) } @@ -50,6 +50,6 @@ export class TSInterfaceHeritage extends Expression { export function isTSInterfaceHeritage(node: AstNode): node is TSInterfaceHeritage { return node instanceof TSInterfaceHeritage } -if (!nodeByType.has(133)) { - nodeByType.set(133, TSInterfaceHeritage) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_HERITAGE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_HERITAGE, TSInterfaceHeritage) +} diff --git a/koala-wrapper/src/generated/peers/TSIntersectionType.ts b/koala-wrapper/src/generated/peers/TSIntersectionType.ts index d21f48d7a75541dfcc2ece04ce9598da34d30670..8f58edf650137880b7566b148d1737c3c31ea916 100644 --- a/koala-wrapper/src/generated/peers/TSIntersectionType.ts +++ b/koala-wrapper/src/generated/peers/TSIntersectionType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSIntersectionType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 113) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERSECTION_TYPE) super(pointer) } @@ -50,6 +50,6 @@ export class TSIntersectionType extends TypeNode { export function isTSIntersectionType(node: AstNode): node is TSIntersectionType { return node instanceof TSIntersectionType } -if (!nodeByType.has(113)) { - nodeByType.set(113, TSIntersectionType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERSECTION_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERSECTION_TYPE, TSIntersectionType) +} diff --git a/koala-wrapper/src/generated/peers/TSLiteralType.ts b/koala-wrapper/src/generated/peers/TSLiteralType.ts index f9ecdf77485b63c51c683013aeaf563ddd6f2cc5..a3ea5c9c177e653eccc864e05a6a4d2434930e52 100644 --- a/koala-wrapper/src/generated/peers/TSLiteralType.ts +++ b/koala-wrapper/src/generated/peers/TSLiteralType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSLiteralType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 109) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE) super(pointer) } @@ -50,6 +50,6 @@ export class TSLiteralType extends TypeNode { export function isTSLiteralType(node: AstNode): node is TSLiteralType { return node instanceof TSLiteralType } -if (!nodeByType.has(109)) { - nodeByType.set(109, TSLiteralType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE, TSLiteralType) +} diff --git a/koala-wrapper/src/generated/peers/TSMappedType.ts b/koala-wrapper/src/generated/peers/TSMappedType.ts index e8a2202dcd50f9b0333ae5321df1fba2159b6d7a..dc8344d7c44a3894eaa7f62c43ef5a2a2f15abc9 100644 --- a/koala-wrapper/src/generated/peers/TSMappedType.ts +++ b/koala-wrapper/src/generated/peers/TSMappedType.ts @@ -34,7 +34,7 @@ import { TSTypeParameter } from "./TSTypeParameter" import { Es2pandaMappedOption } from "./../Es2pandaEnums" export class TSMappedType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 114) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_MAPPED_TYPE) super(pointer) } @@ -60,6 +60,6 @@ export class TSMappedType extends TypeNode { export function isTSMappedType(node: AstNode): node is TSMappedType { return node instanceof TSMappedType } -if (!nodeByType.has(114)) { - nodeByType.set(114, TSMappedType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MAPPED_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MAPPED_TYPE, TSMappedType) +} diff --git a/koala-wrapper/src/generated/peers/TSMethodSignature.ts b/koala-wrapper/src/generated/peers/TSMethodSignature.ts index d16351da03e97a401b644e2bd4f6060be513738d..f0d05604bcd9cd60a9d417c86696c74c01ad9b01 100644 --- a/koala-wrapper/src/generated/peers/TSMethodSignature.ts +++ b/koala-wrapper/src/generated/peers/TSMethodSignature.ts @@ -35,7 +35,7 @@ import { TSTypeParameterDeclaration } from "./TSTypeParameterDeclaration" import { TypeNode } from "./TypeNode" export class TSMethodSignature extends AstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 106) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_METHOD_SIGNATURE) super(pointer) } @@ -67,6 +67,6 @@ export class TSMethodSignature extends AstNode { export function isTSMethodSignature(node: AstNode): node is TSMethodSignature { return node instanceof TSMethodSignature } -if (!nodeByType.has(106)) { - nodeByType.set(106, TSMethodSignature) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_METHOD_SIGNATURE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_METHOD_SIGNATURE, TSMethodSignature) +} diff --git a/koala-wrapper/src/generated/peers/TSModuleBlock.ts b/koala-wrapper/src/generated/peers/TSModuleBlock.ts index d25d54f362608b063c0d4fcda62b1a9cfe5bb411..57f8c51a3b9dc51fc2dc25839bf0a138948f41d0 100644 --- a/koala-wrapper/src/generated/peers/TSModuleBlock.ts +++ b/koala-wrapper/src/generated/peers/TSModuleBlock.ts @@ -32,7 +32,7 @@ import { import { Statement } from "./Statement" export class TSModuleBlock extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 115) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_BLOCK) super(pointer) } @@ -49,6 +49,6 @@ export class TSModuleBlock extends Statement { export function isTSModuleBlock(node: AstNode): node is TSModuleBlock { return node instanceof TSModuleBlock } -if (!nodeByType.has(115)) { - nodeByType.set(115, TSModuleBlock) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_BLOCK)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_BLOCK, TSModuleBlock) +} diff --git a/koala-wrapper/src/generated/peers/TSModuleDeclaration.ts b/koala-wrapper/src/generated/peers/TSModuleDeclaration.ts index c4582a84ce5d6db2bc17856074ef11354fd7bf16..f78854027daab72730f73c7c769d5d9502ce6136 100644 --- a/koala-wrapper/src/generated/peers/TSModuleDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSModuleDeclaration.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class TSModuleDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 123) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_DECLARATION) super(pointer) } @@ -59,6 +59,6 @@ export class TSModuleDeclaration extends Statement { export function isTSModuleDeclaration(node: AstNode): node is TSModuleDeclaration { return node instanceof TSModuleDeclaration } -if (!nodeByType.has(123)) { - nodeByType.set(123, TSModuleDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_MODULE_DECLARATION, TSModuleDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSNamedTupleMember.ts b/koala-wrapper/src/generated/peers/TSNamedTupleMember.ts index 3252f8a474453ec285eeddff87f10df8fb21deef..93085dfa1ddd6fadf9bd2c0078f4bfaca48786b7 100644 --- a/koala-wrapper/src/generated/peers/TSNamedTupleMember.ts +++ b/koala-wrapper/src/generated/peers/TSNamedTupleMember.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSNamedTupleMember extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 135) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER) super(pointer) } @@ -56,6 +56,6 @@ export class TSNamedTupleMember extends TypeNode { export function isTSNamedTupleMember(node: AstNode): node is TSNamedTupleMember { return node instanceof TSNamedTupleMember } -if (!nodeByType.has(135)) { - nodeByType.set(135, TSNamedTupleMember) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NAMED_TUPLE_MEMBER, TSNamedTupleMember) +} diff --git a/koala-wrapper/src/generated/peers/TSNeverKeyword.ts b/koala-wrapper/src/generated/peers/TSNeverKeyword.ts index 39b7de7ff579825bcf9bdc9e03491a55e68e0d4e..039db43d4b6a106cd1ba169325b58485fb7f2211 100644 --- a/koala-wrapper/src/generated/peers/TSNeverKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSNeverKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSNeverKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 99) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_NEVER_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSNeverKeyword extends TypeNode { export function isTSNeverKeyword(node: AstNode): node is TSNeverKeyword { return node instanceof TSNeverKeyword } -if (!nodeByType.has(99)) { - nodeByType.set(99, TSNeverKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NEVER_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NEVER_KEYWORD, TSNeverKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSNonNullExpression.ts b/koala-wrapper/src/generated/peers/TSNonNullExpression.ts index 21bc3e670c2c1beca7edc039e758118cf3d55066..6ed71ba1737606851c0d00ed2aca634a7fa0d6d0 100644 --- a/koala-wrapper/src/generated/peers/TSNonNullExpression.ts +++ b/koala-wrapper/src/generated/peers/TSNonNullExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class TSNonNullExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 100) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_NON_NULL_EXPRESSION) super(pointer) } @@ -54,6 +54,6 @@ export class TSNonNullExpression extends Expression { export function isTSNonNullExpression(node: AstNode): node is TSNonNullExpression { return node instanceof TSNonNullExpression } -if (!nodeByType.has(100)) { - nodeByType.set(100, TSNonNullExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NON_NULL_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NON_NULL_EXPRESSION, TSNonNullExpression) +} diff --git a/koala-wrapper/src/generated/peers/TSNullKeyword.ts b/koala-wrapper/src/generated/peers/TSNullKeyword.ts index c3b161938cd46c004bdbf61c06cf9ecf19a90556..2abd26dfec3b2e18ff2e127322abb001c4b33559 100644 --- a/koala-wrapper/src/generated/peers/TSNullKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSNullKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSNullKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 101) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_NULL_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSNullKeyword extends TypeNode { export function isTSNullKeyword(node: AstNode): node is TSNullKeyword { return node instanceof TSNullKeyword } -if (!nodeByType.has(101)) { - nodeByType.set(101, TSNullKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NULL_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NULL_KEYWORD, TSNullKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSNumberKeyword.ts b/koala-wrapper/src/generated/peers/TSNumberKeyword.ts index ff3c783afef74cea056b86c3d5148561126de94c..6620e5c9861ccd1c5ce5eb505a4bf2c79d6b1329 100644 --- a/koala-wrapper/src/generated/peers/TSNumberKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSNumberKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSNumberKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 90) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_NUMBER_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSNumberKeyword extends TypeNode { export function isTSNumberKeyword(node: AstNode): node is TSNumberKeyword { return node instanceof TSNumberKeyword } -if (!nodeByType.has(90)) { - nodeByType.set(90, TSNumberKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NUMBER_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_NUMBER_KEYWORD, TSNumberKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSObjectKeyword.ts b/koala-wrapper/src/generated/peers/TSObjectKeyword.ts index 2279f62af877839c95f36c7d91265125f088aebe..0a345482bcf8353cf0b7a27771ea1414d9dd5ff7 100644 --- a/koala-wrapper/src/generated/peers/TSObjectKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSObjectKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSObjectKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 97) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_OBJECT_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSObjectKeyword extends TypeNode { export function isTSObjectKeyword(node: AstNode): node is TSObjectKeyword { return node instanceof TSObjectKeyword } -if (!nodeByType.has(97)) { - nodeByType.set(97, TSObjectKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_OBJECT_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_OBJECT_KEYWORD, TSObjectKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSParameterProperty.ts b/koala-wrapper/src/generated/peers/TSParameterProperty.ts index 9efa642c80ed9e8d795a22d3233699f402ad8205..fc86857ecb58d46c2728a1d56c700e6c7b5c508d 100644 --- a/koala-wrapper/src/generated/peers/TSParameterProperty.ts +++ b/koala-wrapper/src/generated/peers/TSParameterProperty.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Es2pandaAccessibilityOption } from "./../Es2pandaEnums" export class TSParameterProperty extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 122) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARAMETER_PROPERTY) super(pointer) } @@ -62,6 +62,6 @@ export class TSParameterProperty extends Expression { export function isTSParameterProperty(node: AstNode): node is TSParameterProperty { return node instanceof TSParameterProperty } -if (!nodeByType.has(122)) { - nodeByType.set(122, TSParameterProperty) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARAMETER_PROPERTY)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARAMETER_PROPERTY, TSParameterProperty) +} diff --git a/koala-wrapper/src/generated/peers/TSParenthesizedType.ts b/koala-wrapper/src/generated/peers/TSParenthesizedType.ts index 043431f7190b8496cbcb584ee0c2a3ea2d95aaf6..1171f2f04e6d3b1fc12205ea857430327ab9f9de 100644 --- a/koala-wrapper/src/generated/peers/TSParenthesizedType.ts +++ b/koala-wrapper/src/generated/peers/TSParenthesizedType.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSParenthesizedType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 108) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARENT_TYPE) super(pointer) } @@ -50,6 +50,6 @@ export class TSParenthesizedType extends TypeNode { export function isTSParenthesizedType(node: AstNode): node is TSParenthesizedType { return node instanceof TSParenthesizedType } -if (!nodeByType.has(108)) { - nodeByType.set(108, TSParenthesizedType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARENT_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PARENT_TYPE, TSParenthesizedType) +} diff --git a/koala-wrapper/src/generated/peers/TSPropertySignature.ts b/koala-wrapper/src/generated/peers/TSPropertySignature.ts index b2725a9645417ce5fee17c8afa2c644de236a3d8..cf57e262dcc690f854bef3b385a0b04c7b4872c5 100644 --- a/koala-wrapper/src/generated/peers/TSPropertySignature.ts +++ b/koala-wrapper/src/generated/peers/TSPropertySignature.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class TSPropertySignature extends AnnotatedAstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 105) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_PROPERTY_SIGNATURE) super(pointer) } @@ -68,6 +68,6 @@ export class TSPropertySignature extends AnnotatedAstNode { export function isTSPropertySignature(node: AstNode): node is TSPropertySignature { return node instanceof TSPropertySignature } -if (!nodeByType.has(105)) { - nodeByType.set(105, TSPropertySignature) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PROPERTY_SIGNATURE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_PROPERTY_SIGNATURE, TSPropertySignature) +} diff --git a/koala-wrapper/src/generated/peers/TSQualifiedName.ts b/koala-wrapper/src/generated/peers/TSQualifiedName.ts index e0f4d87ea5e9511b699996764372bff452f00461..e9e4806efc58bc74094666f1a833c12529783d9e 100644 --- a/koala-wrapper/src/generated/peers/TSQualifiedName.ts +++ b/koala-wrapper/src/generated/peers/TSQualifiedName.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Identifier } from "./Identifier" export class TSQualifiedName extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 129) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_QUALIFIED_NAME) super(pointer) } @@ -53,6 +53,6 @@ export class TSQualifiedName extends Expression { export function isTSQualifiedName(node: AstNode): node is TSQualifiedName { return node instanceof TSQualifiedName } -if (!nodeByType.has(129)) { - nodeByType.set(129, TSQualifiedName) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_QUALIFIED_NAME)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_QUALIFIED_NAME, TSQualifiedName) +} diff --git a/koala-wrapper/src/generated/peers/TSSignatureDeclaration.ts b/koala-wrapper/src/generated/peers/TSSignatureDeclaration.ts index e70bbf176a592c02f77edb2e944710c18ee7d61d..053a2dc3de920efc52c2cf8ca186d84a35db9388 100644 --- a/koala-wrapper/src/generated/peers/TSSignatureDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSSignatureDeclaration.ts @@ -37,7 +37,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class TSSignatureDeclaration extends TypedAstNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 107) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_SIGNATURE_DECLARATION) super(pointer) } @@ -63,6 +63,6 @@ export class TSSignatureDeclaration extends TypedAstNode { export function isTSSignatureDeclaration(node: AstNode): node is TSSignatureDeclaration { return node instanceof TSSignatureDeclaration } -if (!nodeByType.has(107)) { - nodeByType.set(107, TSSignatureDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_SIGNATURE_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_SIGNATURE_DECLARATION, TSSignatureDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSStringKeyword.ts b/koala-wrapper/src/generated/peers/TSStringKeyword.ts index 6f749be3375069de13c963d46136baeeff3e55bf..71fb6c6b13c20bd739a3465406b3d9532956d34f 100644 --- a/koala-wrapper/src/generated/peers/TSStringKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSStringKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSStringKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 92) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_STRING_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSStringKeyword extends TypeNode { export function isTSStringKeyword(node: AstNode): node is TSStringKeyword { return node instanceof TSStringKeyword } -if (!nodeByType.has(92)) { - nodeByType.set(92, TSStringKeyword) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_STRING_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_STRING_KEYWORD, TSStringKeyword) +} diff --git a/koala-wrapper/src/generated/peers/TSThisType.ts b/koala-wrapper/src/generated/peers/TSThisType.ts index e57be0ab0769bb28bf8af339956c43c0ecfa1ddc..7a87d159c4be05a3fdfa1a24b308cbce15d5d7aa 100644 --- a/koala-wrapper/src/generated/peers/TSThisType.ts +++ b/koala-wrapper/src/generated/peers/TSThisType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSThisType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 116) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_THIS_TYPE) super(pointer) } @@ -46,6 +46,6 @@ export class TSThisType extends TypeNode { export function isTSThisType(node: AstNode): node is TSThisType { return node instanceof TSThisType } -if (!nodeByType.has(116)) { - nodeByType.set(116, TSThisType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_THIS_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_THIS_TYPE, TSThisType) +} diff --git a/koala-wrapper/src/generated/peers/TSTupleType.ts b/koala-wrapper/src/generated/peers/TSTupleType.ts index 11bafff086b5b703e40a65c6868ef9ade06afaf3..8780f3e6627259f0fe159db7fecfb5402facfc75 100644 --- a/koala-wrapper/src/generated/peers/TSTupleType.ts +++ b/koala-wrapper/src/generated/peers/TSTupleType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSTupleType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 134) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TUPLE_TYPE) super(pointer) } @@ -49,6 +49,6 @@ export class TSTupleType extends TypeNode { export function isTSTupleType(node: AstNode): node is TSTupleType { return node instanceof TSTupleType } -if (!nodeByType.has(134)) { - nodeByType.set(134, TSTupleType) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TUPLE_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TUPLE_TYPE, TSTupleType) +} diff --git a/koala-wrapper/src/generated/peers/TSTypeAliasDeclaration.ts b/koala-wrapper/src/generated/peers/TSTypeAliasDeclaration.ts index 58e381c2daac9781394f57079114de0410c47e7e..025686869f95f02ef0a90719c020f445e08f04f7 100644 --- a/koala-wrapper/src/generated/peers/TSTypeAliasDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSTypeAliasDeclaration.ts @@ -37,7 +37,7 @@ import { Decorator } from "./Decorator" import { AnnotationUsage } from "./AnnotationUsage" export class TSTypeAliasDeclaration extends AnnotatedStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 127) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION) super(pointer) } @@ -87,6 +87,6 @@ export class TSTypeAliasDeclaration extends AnnotatedStatement { export function isTSTypeAliasDeclaration(node: AstNode): node is TSTypeAliasDeclaration { return node instanceof TSTypeAliasDeclaration } -if (!nodeByType.has(127)) { - nodeByType.set(127, TSTypeAliasDeclaration) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, TSTypeAliasDeclaration) +} diff --git a/koala-wrapper/src/generated/peers/TSTypeAssertion.ts b/koala-wrapper/src/generated/peers/TSTypeAssertion.ts index e01615f9b2578d4bcfa371dc950a886ed77d8623..05bb8207b491d36434138a8f8a0a670ec7da65b7 100644 --- a/koala-wrapper/src/generated/peers/TSTypeAssertion.ts +++ b/koala-wrapper/src/generated/peers/TSTypeAssertion.ts @@ -34,7 +34,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSTypeAssertion extends AnnotatedExpression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 140) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ASSERTION) super(pointer) } @@ -59,6 +59,6 @@ export class TSTypeAssertion extends AnnotatedExpression { export function isTSTypeAssertion(node: AstNode): node is TSTypeAssertion { return node instanceof TSTypeAssertion } -if (!nodeByType.has(140)) { - nodeByType.set(140, TSTypeAssertion) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ASSERTION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ASSERTION, TSTypeAssertion) +} diff --git a/koala-wrapper/src/generated/peers/TSTypeLiteral.ts b/koala-wrapper/src/generated/peers/TSTypeLiteral.ts index 20e3a4f22977e760627448c3dfeafd8b4c0470c7..c6f790a26e02ee4200b1bf7748314476d5692068 100644 --- a/koala-wrapper/src/generated/peers/TSTypeLiteral.ts +++ b/koala-wrapper/src/generated/peers/TSTypeLiteral.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSTypeLiteral extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 104) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE) super(pointer) } @@ -49,6 +49,6 @@ export class TSTypeLiteral extends TypeNode { export function isTSTypeLiteral(node: AstNode): node is TSTypeLiteral { return node instanceof TSTypeLiteral } -if (!nodeByType.has(104)) { - nodeByType.set(104, TSTypeLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_LITERAL_TYPE, TSTypeLiteral) +} diff --git a/koala-wrapper/src/generated/peers/TSTypeOperator.ts b/koala-wrapper/src/generated/peers/TSTypeOperator.ts index 3f94d244fb67cdb10966ceb3b3838c4d1a4c9aec..e082c1075a295150642b25dc8b7f65139ac89879 100644 --- a/koala-wrapper/src/generated/peers/TSTypeOperator.ts +++ b/koala-wrapper/src/generated/peers/TSTypeOperator.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Es2pandaTSOperatorType } from "./../Es2pandaEnums" export class TSTypeOperator extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 117) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_OPERATOR) super(pointer) } @@ -59,6 +59,6 @@ export class TSTypeOperator extends TypeNode { export function isTSTypeOperator(node: AstNode): node is TSTypeOperator { return node instanceof TSTypeOperator } -if (!nodeByType.has(117)) { - nodeByType.set(117, TSTypeOperator) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_OPERATOR)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_OPERATOR, TSTypeOperator) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypeParameter.ts b/koala-wrapper/src/generated/peers/TSTypeParameter.ts index 28012faf1144a098154433ebe27e6d0ca8f39061..7451ad017e0aabe2800d2f2585b26e9506250dd1 100644 --- a/koala-wrapper/src/generated/peers/TSTypeParameter.ts +++ b/koala-wrapper/src/generated/peers/TSTypeParameter.ts @@ -36,7 +36,7 @@ import { Es2pandaModifierFlags } from "./../Es2pandaEnums" import { AnnotationUsage } from "./AnnotationUsage" export class TSTypeParameter extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 118) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER) super(pointer) } @@ -83,6 +83,6 @@ export class TSTypeParameter extends Expression { export function isTSTypeParameter(node: AstNode): node is TSTypeParameter { return node instanceof TSTypeParameter } -if (!nodeByType.has(118)) { - nodeByType.set(118, TSTypeParameter) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER, TSTypeParameter) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypeParameterDeclaration.ts b/koala-wrapper/src/generated/peers/TSTypeParameterDeclaration.ts index 59f4ab594c7ec5a54a8864009ca53acd83df8db3..61f91a8b6569050934abfc9eb2b55a321c422926 100644 --- a/koala-wrapper/src/generated/peers/TSTypeParameterDeclaration.ts +++ b/koala-wrapper/src/generated/peers/TSTypeParameterDeclaration.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TSTypeParameter } from "./TSTypeParameter" export class TSTypeParameterDeclaration extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 119) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION) super(pointer) } @@ -58,6 +58,6 @@ export class TSTypeParameterDeclaration extends Expression { export function isTSTypeParameterDeclaration(node: AstNode): node is TSTypeParameterDeclaration { return node instanceof TSTypeParameterDeclaration } -if (!nodeByType.has(119)) { - nodeByType.set(119, TSTypeParameterDeclaration) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_DECLARATION, TSTypeParameterDeclaration) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypeParameterInstantiation.ts b/koala-wrapper/src/generated/peers/TSTypeParameterInstantiation.ts index b6a23b3290a713a83f583958e38b52d8ae64f7dc..8c30a3aa06fba0916d18e0fd8f01ecf2c4a26ed0 100644 --- a/koala-wrapper/src/generated/peers/TSTypeParameterInstantiation.ts +++ b/koala-wrapper/src/generated/peers/TSTypeParameterInstantiation.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TypeNode } from "./TypeNode" export class TSTypeParameterInstantiation extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 120) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION) super(pointer) } @@ -50,6 +50,6 @@ export class TSTypeParameterInstantiation extends Expression { export function isTSTypeParameterInstantiation(node: AstNode): node is TSTypeParameterInstantiation { return node instanceof TSTypeParameterInstantiation } -if (!nodeByType.has(120)) { - nodeByType.set(120, TSTypeParameterInstantiation) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PARAMETER_INSTANTIATION, TSTypeParameterInstantiation) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypePredicate.ts b/koala-wrapper/src/generated/peers/TSTypePredicate.ts index 551d62f98d6dd04b6a45ab9658371247b41b57a2..27ce97a73960c3fde47d11ede8b779e82fa3df0c 100644 --- a/koala-wrapper/src/generated/peers/TSTypePredicate.ts +++ b/koala-wrapper/src/generated/peers/TSTypePredicate.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSTypePredicate extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 121) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PREDICATE) super(pointer) } @@ -56,6 +56,6 @@ export class TSTypePredicate extends TypeNode { export function isTSTypePredicate(node: AstNode): node is TSTypePredicate { return node instanceof TSTypePredicate } -if (!nodeByType.has(121)) { - nodeByType.set(121, TSTypePredicate) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PREDICATE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_PREDICATE, TSTypePredicate) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypeQuery.ts b/koala-wrapper/src/generated/peers/TSTypeQuery.ts index d4576d1fa5d5ec3cb7021711418d91bb7986477e..603124f7f55e5ec0439e86c6e5774251be97f4dc 100644 --- a/koala-wrapper/src/generated/peers/TSTypeQuery.ts +++ b/koala-wrapper/src/generated/peers/TSTypeQuery.ts @@ -33,7 +33,7 @@ import { TypeNode } from "./TypeNode" import { Expression } from "./Expression" export class TSTypeQuery extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 137) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_QUERY) super(pointer) } @@ -50,6 +50,6 @@ export class TSTypeQuery extends TypeNode { export function isTSTypeQuery(node: AstNode): node is TSTypeQuery { return node instanceof TSTypeQuery } -if (!nodeByType.has(137)) { - nodeByType.set(137, TSTypeQuery) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_QUERY)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_QUERY, TSTypeQuery) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSTypeReference.ts b/koala-wrapper/src/generated/peers/TSTypeReference.ts index 50e62aafbb811bffec9210f41c89289d06e7dd18..f70f708c7eafb55d8ede53f0fd76b076c1887786 100644 --- a/koala-wrapper/src/generated/peers/TSTypeReference.ts +++ b/koala-wrapper/src/generated/peers/TSTypeReference.ts @@ -35,7 +35,7 @@ import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" import { Identifier } from "./Identifier" export class TSTypeReference extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 128) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_REFERENCE) super(pointer) } @@ -55,6 +55,6 @@ export class TSTypeReference extends TypeNode { export function isTSTypeReference(node: AstNode): node is TSTypeReference { return node instanceof TSTypeReference } -if (!nodeByType.has(128)) { - nodeByType.set(128, TSTypeReference) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_REFERENCE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_REFERENCE, TSTypeReference) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSUndefinedKeyword.ts b/koala-wrapper/src/generated/peers/TSUndefinedKeyword.ts index 9129d02d9ac35cff4a89493197591df481ee90a1..c7809258a1ff63431897f6d0efcf1240a3ed5439 100644 --- a/koala-wrapper/src/generated/peers/TSUndefinedKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSUndefinedKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSUndefinedKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 95) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNDEFINED_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSUndefinedKeyword extends TypeNode { export function isTSUndefinedKeyword(node: AstNode): node is TSUndefinedKeyword { return node instanceof TSUndefinedKeyword } -if (!nodeByType.has(95)) { - nodeByType.set(95, TSUndefinedKeyword) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNDEFINED_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNDEFINED_KEYWORD, TSUndefinedKeyword) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSUnionType.ts b/koala-wrapper/src/generated/peers/TSUnionType.ts index 406ecc4162a0e2c989a0e6219472c2bdffe785f7..df5479501a4bf76f61efba9b87e2eef7014b38fd 100644 --- a/koala-wrapper/src/generated/peers/TSUnionType.ts +++ b/koala-wrapper/src/generated/peers/TSUnionType.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSUnionType extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 103) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNION_TYPE) super(pointer) } @@ -49,6 +49,6 @@ export class TSUnionType extends TypeNode { export function isTSUnionType(node: AstNode): node is TSUnionType { return node instanceof TSUnionType } -if (!nodeByType.has(103)) { - nodeByType.set(103, TSUnionType) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNION_TYPE)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNION_TYPE, TSUnionType) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSUnknownKeyword.ts b/koala-wrapper/src/generated/peers/TSUnknownKeyword.ts index 0371b243c7e4196d5f8ba6e1bbbf8b9b354957e4..0eb71b7d2a285bc498665931201313e01c1d92ce 100644 --- a/koala-wrapper/src/generated/peers/TSUnknownKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSUnknownKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSUnknownKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 96) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNKNOWN_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSUnknownKeyword extends TypeNode { export function isTSUnknownKeyword(node: AstNode): node is TSUnknownKeyword { return node instanceof TSUnknownKeyword } -if (!nodeByType.has(96)) { - nodeByType.set(96, TSUnknownKeyword) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNKNOWN_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_UNKNOWN_KEYWORD, TSUnknownKeyword) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TSVoidKeyword.ts b/koala-wrapper/src/generated/peers/TSVoidKeyword.ts index 605cceb11b90ef9c49621003d894851e7868b970..1cfe4091d63d1ed1f0686312d796251c9fe53594 100644 --- a/koala-wrapper/src/generated/peers/TSVoidKeyword.ts +++ b/koala-wrapper/src/generated/peers/TSVoidKeyword.ts @@ -32,7 +32,7 @@ import { import { TypeNode } from "./TypeNode" export class TSVoidKeyword extends TypeNode { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 94) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_VOID_KEYWORD) super(pointer) } @@ -46,6 +46,6 @@ export class TSVoidKeyword extends TypeNode { export function isTSVoidKeyword(node: AstNode): node is TSVoidKeyword { return node instanceof TSVoidKeyword } -if (!nodeByType.has(94)) { - nodeByType.set(94, TSVoidKeyword) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TS_VOID_KEYWORD)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TS_VOID_KEYWORD, TSVoidKeyword) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TaggedTemplateExpression.ts b/koala-wrapper/src/generated/peers/TaggedTemplateExpression.ts index 0ccbf0f084a39507e617f4fbccc8e60974081b0f..1f1a7dcc1b3cee594f75cc3692b1a285f8511a13 100644 --- a/koala-wrapper/src/generated/peers/TaggedTemplateExpression.ts +++ b/koala-wrapper/src/generated/peers/TaggedTemplateExpression.ts @@ -34,7 +34,7 @@ import { TemplateLiteral } from "./TemplateLiteral" import { TSTypeParameterInstantiation } from "./TSTypeParameterInstantiation" export class TaggedTemplateExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 141) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION) super(pointer) } @@ -57,6 +57,6 @@ export class TaggedTemplateExpression extends Expression { export function isTaggedTemplateExpression(node: AstNode): node is TaggedTemplateExpression { return node instanceof TaggedTemplateExpression } -if (!nodeByType.has(141)) { - nodeByType.set(141, TaggedTemplateExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TAGGED_TEMPLATE_EXPRESSION, TaggedTemplateExpression) +} diff --git a/koala-wrapper/src/generated/peers/TemplateElement.ts b/koala-wrapper/src/generated/peers/TemplateElement.ts index 0c4108f47471576d805a3c290be3d15705e76ad6..32602cccc4ae8dbab7bb6b9fdc9b569907883fab 100644 --- a/koala-wrapper/src/generated/peers/TemplateElement.ts +++ b/koala-wrapper/src/generated/peers/TemplateElement.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class TemplateElement extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 142) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_ELEMENT) super(pointer) } @@ -58,6 +58,6 @@ export class TemplateElement extends Expression { export function isTemplateElement(node: AstNode): node is TemplateElement { return node instanceof TemplateElement } -if (!nodeByType.has(142)) { - nodeByType.set(142, TemplateElement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_ELEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_ELEMENT, TemplateElement) +} diff --git a/koala-wrapper/src/generated/peers/TemplateLiteral.ts b/koala-wrapper/src/generated/peers/TemplateLiteral.ts index 58039a87e8a28827d17e774132da15dc84c942bf..b51bb606fec85a23efcf12c3372a68a4b0e618b9 100644 --- a/koala-wrapper/src/generated/peers/TemplateLiteral.ts +++ b/koala-wrapper/src/generated/peers/TemplateLiteral.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { TemplateElement } from "./TemplateElement" export class TemplateLiteral extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 143) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_LITERAL) super(pointer) } @@ -49,10 +49,13 @@ export class TemplateLiteral extends Expression { get expressions(): readonly Expression[] { return unpackNodeArray(global.generatedEs2panda._TemplateLiteralExpressionsConst(global.context, this.peer)) } + get multilineString(): string { + return unpackString(global.generatedEs2panda._TemplateLiteralGetMultilineStringConst(global.context, this.peer)); + } } export function isTemplateLiteral(node: AstNode): node is TemplateLiteral { return node instanceof TemplateLiteral } -if (!nodeByType.has(143)) { - nodeByType.set(143, TemplateLiteral) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TEMPLATE_LITERAL, TemplateLiteral) +} diff --git a/koala-wrapper/src/generated/peers/ThisExpression.ts b/koala-wrapper/src/generated/peers/ThisExpression.ts index 1ec6476d8e03323b675606d4a6b9ff789e262960..c16668c4fb737615e6efdefb45bdd0b973295ba7 100644 --- a/koala-wrapper/src/generated/peers/ThisExpression.ts +++ b/koala-wrapper/src/generated/peers/ThisExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class ThisExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 144) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_THIS_EXPRESSION) super(pointer) } @@ -46,6 +46,6 @@ export class ThisExpression extends Expression { export function isThisExpression(node: AstNode): node is ThisExpression { return node instanceof ThisExpression } -if (!nodeByType.has(144)) { - nodeByType.set(144, ThisExpression) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_THIS_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_THIS_EXPRESSION, ThisExpression) +} diff --git a/koala-wrapper/src/generated/peers/ThrowStatement.ts b/koala-wrapper/src/generated/peers/ThrowStatement.ts index 74cc30e59fcf46999c0083b8927532eb64884a07..e4f40a1fb2017dd947e6b7970b46d75bf58d1f4f 100644 --- a/koala-wrapper/src/generated/peers/ThrowStatement.ts +++ b/koala-wrapper/src/generated/peers/ThrowStatement.ts @@ -33,7 +33,7 @@ import { Statement } from "./Statement" import { Expression } from "./Expression" export class ThrowStatement extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 146) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_THROW_STATEMENT) super(pointer) } @@ -50,6 +50,6 @@ export class ThrowStatement extends Statement { export function isThrowStatement(node: AstNode): node is ThrowStatement { return node instanceof ThrowStatement } -if (!nodeByType.has(146)) { - nodeByType.set(146, ThrowStatement) -} \ No newline at end of file +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_THROW_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_THROW_STATEMENT, ThrowStatement) +} diff --git a/koala-wrapper/src/generated/peers/TryStatement.ts b/koala-wrapper/src/generated/peers/TryStatement.ts index c855b8bfac2c8ce1dbbd0ce10a36199f1f739677..93bde0b0011a5fe0591171d6ae4129ac13325fad 100644 --- a/koala-wrapper/src/generated/peers/TryStatement.ts +++ b/koala-wrapper/src/generated/peers/TryStatement.ts @@ -22,21 +22,30 @@ import { unpackNodeArray, assertValidPeer, AstNode, - Es2pandaAstNodeType, KNativePointer, nodeByType, ArktsObject, - unpackString + unpackString, + Es2pandaAstNodeType } from "../../reexport-for-generated" -import { Statement } from "./Statement" import { BlockStatement } from "./BlockStatement" import { CatchClause } from "./CatchClause" +import { LabelPair } from "./LabelPair" +import { Statement } from "./Statement" export class TryStatement extends Statement { - constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 147) + constructor(pointer: KNativePointer) { + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TRY_STATEMENT) super(pointer) - + } + static createTryStatement(block: BlockStatement | undefined, catchClauses: readonly CatchClause[], finalizer: BlockStatement | undefined, finalizerInsertionsLabelPair: readonly LabelPair[], finalizerInsertionsStatement: readonly Statement[]): TryStatement { + return new TryStatement(global.generatedEs2panda._CreateTryStatement(global.context, passNode(block), passNodeArray(catchClauses), catchClauses.length, passNode(finalizer), passNodeArray(finalizerInsertionsLabelPair), finalizerInsertionsLabelPair.length, passNodeArray(finalizerInsertionsStatement), finalizerInsertionsStatement.length)) + } + static updateTryStatement(original: TryStatement | undefined, block: BlockStatement | undefined, catchClauses: readonly CatchClause[], finalizer: BlockStatement | undefined, finalizerInsertionsLabelPair: readonly LabelPair[], finalizerInsertionsStatement: readonly Statement[]): TryStatement { + return new TryStatement(global.generatedEs2panda._UpdateTryStatement(global.context, passNode(original), passNode(block), passNodeArray(catchClauses), catchClauses.length, passNode(finalizer), passNodeArray(finalizerInsertionsLabelPair), finalizerInsertionsLabelPair.length, passNodeArray(finalizerInsertionsStatement), finalizerInsertionsStatement.length)) + } + static update1TryStatement(original?: TryStatement, other?: TryStatement): TryStatement { + return new TryStatement(global.generatedEs2panda._UpdateTryStatement1(global.context, passNode(original), passNode(other))) } get finallyBlock(): BlockStatement | undefined { return unpackNode(global.generatedEs2panda._TryStatementFinallyBlockConst(global.context, this.peer)) @@ -47,6 +56,9 @@ export class TryStatement extends Statement { get hasFinalizer(): boolean { return global.generatedEs2panda._TryStatementHasFinalizerConst(global.context, this.peer) } + get hasDefaultCatchClause(): boolean { + return global.generatedEs2panda._TryStatementHasDefaultCatchClauseConst(global.context, this.peer); + } get catchClauses(): readonly CatchClause[] { return unpackNodeArray(global.generatedEs2panda._TryStatementCatchClausesConst(global.context, this.peer)) } @@ -59,9 +71,9 @@ export class TryStatement extends Statement { return this } } -export function isTryStatement(node: AstNode): node is TryStatement { +export function isTryStatement(node: object | undefined): node is TryStatement { return node instanceof TryStatement } -if (!nodeByType.has(147)) { - nodeByType.set(147, TryStatement) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TRY_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TRY_STATEMENT, TryStatement) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/TypeofExpression.ts b/koala-wrapper/src/generated/peers/TypeofExpression.ts index 0c80ae8e83170f69e5936c29cb8084b3e601ba68..8496fe19c9c0394cce5c6abf2ef6629f2afdd2fa 100644 --- a/koala-wrapper/src/generated/peers/TypeofExpression.ts +++ b/koala-wrapper/src/generated/peers/TypeofExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class TypeofExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 145) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_TYPEOF_EXPRESSION) super(pointer) } @@ -49,6 +49,6 @@ export class TypeofExpression extends Expression { export function isTypeofExpression(node: AstNode): node is TypeofExpression { return node instanceof TypeofExpression } -if (!nodeByType.has(145)) { - nodeByType.set(145, TypeofExpression) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_TYPEOF_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_TYPEOF_EXPRESSION, TypeofExpression) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/UnaryExpression.ts b/koala-wrapper/src/generated/peers/UnaryExpression.ts index 2b2d1aec788ecc7641495eeaadec082b31b53037..88a9071dc2fe7a88d6bdf4b2717d31ae12bcda1a 100644 --- a/koala-wrapper/src/generated/peers/UnaryExpression.ts +++ b/koala-wrapper/src/generated/peers/UnaryExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Es2pandaTokenType } from "./../Es2pandaEnums" export class UnaryExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 148) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_UNARY_EXPRESSION) super(pointer) } @@ -53,6 +53,6 @@ export class UnaryExpression extends Expression { export function isUnaryExpression(node: AstNode): node is UnaryExpression { return node instanceof UnaryExpression } -if (!nodeByType.has(148)) { - nodeByType.set(148, UnaryExpression) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_UNARY_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_UNARY_EXPRESSION, UnaryExpression) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/UndefinedLiteral.ts b/koala-wrapper/src/generated/peers/UndefinedLiteral.ts index 833805c1ff1cbdddb12fae98a2626b7d1f9b2c6a..b50432720e8509ce064cd19997b39f204ea38a7b 100644 --- a/koala-wrapper/src/generated/peers/UndefinedLiteral.ts +++ b/koala-wrapper/src/generated/peers/UndefinedLiteral.ts @@ -32,7 +32,7 @@ import { import { Literal } from "./Literal" export class UndefinedLiteral extends Literal { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 51) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL) super(pointer) } @@ -46,6 +46,6 @@ export class UndefinedLiteral extends Literal { export function isUndefinedLiteral(node: AstNode): node is UndefinedLiteral { return node instanceof UndefinedLiteral } -if (!nodeByType.has(51)) { - nodeByType.set(51, UndefinedLiteral) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL, UndefinedLiteral) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/UpdateExpression.ts b/koala-wrapper/src/generated/peers/UpdateExpression.ts index 5c1b8d6f1c5c87463527d3922021a80f379727d7..30d9f1839608a6b45e44d8780e8909966c1a8801 100644 --- a/koala-wrapper/src/generated/peers/UpdateExpression.ts +++ b/koala-wrapper/src/generated/peers/UpdateExpression.ts @@ -33,7 +33,7 @@ import { Expression } from "./Expression" import { Es2pandaTokenType } from "./../Es2pandaEnums" export class UpdateExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 149) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_UPDATE_EXPRESSION) super(pointer) } @@ -56,6 +56,6 @@ export class UpdateExpression extends Expression { export function isUpdateExpression(node: AstNode): node is UpdateExpression { return node instanceof UpdateExpression } -if (!nodeByType.has(149)) { - nodeByType.set(149, UpdateExpression) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_UPDATE_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_UPDATE_EXPRESSION, UpdateExpression) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/VariableDeclaration.ts b/koala-wrapper/src/generated/peers/VariableDeclaration.ts index 751968ae5a36ac907329366d9faa250f4aef6da5..6699e1f552ed86a00927b96d62758cc720ff1acd 100644 --- a/koala-wrapper/src/generated/peers/VariableDeclaration.ts +++ b/koala-wrapper/src/generated/peers/VariableDeclaration.ts @@ -36,7 +36,7 @@ import { Decorator } from "./Decorator" import { AnnotationUsage } from "./AnnotationUsage" export class VariableDeclaration extends Statement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 150) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATION) super(pointer) } @@ -67,6 +67,6 @@ export class VariableDeclaration extends Statement { export function isVariableDeclaration(node: AstNode): node is VariableDeclaration { return node instanceof VariableDeclaration } -if (!nodeByType.has(150)) { - nodeByType.set(150, VariableDeclaration) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATION, VariableDeclaration) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/VariableDeclarator.ts b/koala-wrapper/src/generated/peers/VariableDeclarator.ts index e0d9cb0778164a2f4cce199f4d7c20550d8de92c..3913fc3d6e929dce2fe47e0b67a8bd51cdb48358 100644 --- a/koala-wrapper/src/generated/peers/VariableDeclarator.ts +++ b/koala-wrapper/src/generated/peers/VariableDeclarator.ts @@ -34,7 +34,7 @@ import { Es2pandaVariableDeclaratorFlag } from "./../Es2pandaEnums" import { Expression } from "./Expression" export class VariableDeclarator extends TypedStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 151) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR) super(pointer) } @@ -68,6 +68,6 @@ export class VariableDeclarator extends TypedStatement { export function isVariableDeclarator(node: AstNode): node is VariableDeclarator { return node instanceof VariableDeclarator } -if (!nodeByType.has(151)) { - nodeByType.set(151, VariableDeclarator) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR, VariableDeclarator) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/WhileStatement.ts b/koala-wrapper/src/generated/peers/WhileStatement.ts index f71a1d5dded18b3cb47b347b26759eb0630b0a8d..548fd8bf2d4244125121c186352a577f71cdb152 100644 --- a/koala-wrapper/src/generated/peers/WhileStatement.ts +++ b/koala-wrapper/src/generated/peers/WhileStatement.ts @@ -34,7 +34,7 @@ import { Expression } from "./Expression" import { Statement } from "./Statement" export class WhileStatement extends LoopStatement { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 152) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_WHILE_STATEMENT) super(pointer) } @@ -54,6 +54,6 @@ export class WhileStatement extends LoopStatement { export function isWhileStatement(node: AstNode): node is WhileStatement { return node instanceof WhileStatement } -if (!nodeByType.has(152)) { - nodeByType.set(152, WhileStatement) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_WHILE_STATEMENT)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_WHILE_STATEMENT, WhileStatement) } \ No newline at end of file diff --git a/koala-wrapper/src/generated/peers/YieldExpression.ts b/koala-wrapper/src/generated/peers/YieldExpression.ts index 55c0b9022e7c041bdb27b05c0587f30eff6cec32..90846a9333b282a2ba870cd2d51b8e6ea474b643 100644 --- a/koala-wrapper/src/generated/peers/YieldExpression.ts +++ b/koala-wrapper/src/generated/peers/YieldExpression.ts @@ -32,7 +32,7 @@ import { import { Expression } from "./Expression" export class YieldExpression extends Expression { constructor(pointer: KNativePointer) { - assertValidPeer(pointer, 153) + assertValidPeer(pointer, Es2pandaAstNodeType.AST_NODE_TYPE_YIELD_EXPRESSION) super(pointer) } @@ -52,6 +52,6 @@ export class YieldExpression extends Expression { export function isYieldExpression(node: AstNode): node is YieldExpression { return node instanceof YieldExpression } -if (!nodeByType.has(153)) { - nodeByType.set(153, YieldExpression) +if (!nodeByType.has(Es2pandaAstNodeType.AST_NODE_TYPE_YIELD_EXPRESSION)) { + nodeByType.set(Es2pandaAstNodeType.AST_NODE_TYPE_YIELD_EXPRESSION, YieldExpression) } \ No newline at end of file