diff --git a/arkui-plugins/.gitignore b/arkui-plugins/.gitignore index 98f9b3e907e12eb5eaac00e962654cb76f9091a0..7a115dac798dbfa3a37ede790d50954e03b13f9a 100644 --- a/arkui-plugins/.gitignore +++ b/arkui-plugins/.gitignore @@ -8,6 +8,7 @@ build/ lib/ *.tgz +*.log package-lock.json /**/*/package-lock.json @@ -17,3 +18,8 @@ coverage/ **/*/report test/demo/localtest/build_config.json +test/demo/localtest/build_decl_config.json +test/demo/hello_world + +test/mirror/mirror_decl_config.json +test/mirror/demo/hello_world diff --git a/arkui-plugins/BUILD.gn b/arkui-plugins/BUILD.gn index 707355e1d2cf1ab6080c64b7799320b8f742e4c9..ee92b4b7d97cdd9bfcd329a12f34aa0ae8b93ae0 100755 --- a/arkui-plugins/BUILD.gn +++ b/arkui-plugins/BUILD.gn @@ -50,8 +50,28 @@ action("build_ets_sysResource") { ] } +action("test_koala_mirror") { + deps = [ ":gen_ui_plugins" ] + script = "//developtools/ace_ets2bundle/test_koala_mirror.py" + outputs = [ "$target_gen_dir" ] + args = [ + "--test_path", + rebase_path(get_path_info("./test", "abspath")), + "--npm", + rebase_path(npm_path), + ] +} + ohos_copy("ui_plugin") { - deps = [":gen_ui_plugins", ":build_ets_sysResource" ] + deps = [ + ":gen_ui_plugins", + ":build_ets_sysResource" + ] + + if (is_linux) { + deps += [":test_koala_mirror"] + } + sources = [ rebase_path("$target_gen_dir") ] outputs = [ target_out_dir + "/$target_name" ] module_source_dir = target_out_dir + "/$target_name" diff --git a/arkui-plugins/custom-import-plugin.js b/arkui-plugins/custom-import-plugin.js index 84b8a6bd5847b908683c89111b4447e2b3ac87b3..510df2141ddaeee7af40784d2ebc61cfa73b4e3a 100644 --- a/arkui-plugins/custom-import-plugin.js +++ b/arkui-plugins/custom-import-plugin.js @@ -37,7 +37,25 @@ module.exports = function (babel) { const requireCall = t.callExpression(t.identifier('require'), [t.callExpression(t.identifier('getArktsPath'), [])]); const arkts = t.variableDeclaration('const', [ - t.variableDeclarator(t.identifier('arkts'), requireCall) + t.variableDeclarator(t.identifier(pathNode.node.specifiers[0].local.name), requireCall) + ]); + + pathNode.replaceWithMultiple([newImport, arkts]); + } + if (sourceValue === '@koalaui/libarkts-mirror' && pathNode.node.specifiers.length === 1 && t.isImportNamespaceSpecifier(pathNode.node.specifiers[0])) { + const currentFileDir = path.dirname(pathNode.hub.file.opts.filename); + const configDir = process.cwd(); + const relativePath = path.relative(currentFileDir, configDir); + const importPath = relativePath ? path.join(relativePath, 'path') : './path'; + + const newImport = t.importDeclaration( + [t.importSpecifier(t.identifier('getArktsMirrorPath'), t.identifier('getArktsMirrorPath'))], + t.stringLiteral(importPath) + ); + + const requireCall = t.callExpression(t.identifier('require'), [t.callExpression(t.identifier('getArktsMirrorPath'), [])]); + const arkts = t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(pathNode.node.specifiers[0].local.name), requireCall) ]); pathNode.replaceWithMultiple([newImport, arkts]); diff --git a/arkui-plugins/mirror-replace/common/abstract-visitor.ts b/arkui-plugins/mirror-replace/common/abstract-visitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba4cc27e21ee966464102227f9e6deea09821a15 --- /dev/null +++ b/arkui-plugins/mirror-replace/common/abstract-visitor.ts @@ -0,0 +1,53 @@ +/* + * 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 mirrorArkts from '@koalaui/libarkts-mirror'; + +export interface VisitorOptions { + isExternal?: boolean; + externalSourceName?: string; + program?: mirrorArkts.Program; +} + +export abstract class AbstractVisitor implements VisitorOptions { + public isExternal: boolean; + public externalSourceName?: string; + public program?: mirrorArkts.Program; + + constructor(options?: VisitorOptions) { + this.isExternal = options?.isExternal ?? false; + this.externalSourceName = options?.externalSourceName; + this.program = options?.program; + } + + indentation = 0; + + withIndentation(exec: () => T) { + this.indentation++; + const result = exec(); + this.indentation--; + return result; + } + + abstract visitor(node: mirrorArkts.AstNode): mirrorArkts.AstNode; + + reset(): void { + this.indentation = 0; + } + + visitEachChild(node: mirrorArkts.AstNode): mirrorArkts.AstNode { + return this.withIndentation(() => mirrorArkts.visitEachChild(node, (it) => this.visitor(it))); + } +} diff --git a/arkui-plugins/mirror-replace/common/arkts-utils.ts b/arkui-plugins/mirror-replace/common/arkts-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..aaa6f23c76a0878c3e467dbe1fac428620efea30 --- /dev/null +++ b/arkui-plugins/mirror-replace/common/arkts-utils.ts @@ -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. + */ + +export function matchPrefix(prefixCollection: (string | RegExp)[], name: string): boolean { + for (const prefix of prefixCollection) { + let regex: RegExp; + + if (typeof prefix === 'string') { + regex = new RegExp('^' + prefix); + } else { + regex = new RegExp('^' + prefix.source); + } + + if (regex.test(name)) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/arkui-plugins/mirror-replace/common/compat-koala-wrapper.ts b/arkui-plugins/mirror-replace/common/compat-koala-wrapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d84ac252a47763d2973ae5303462782583898cb --- /dev/null +++ b/arkui-plugins/mirror-replace/common/compat-koala-wrapper.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. + */ + +import * as arkts from '@koalaui/libarkts'; +import * as mirrorArkts from '@koalaui/libarkts-mirror'; +import { PluginContext } from './plugin-context'; + +export function copyGlobalFromKoalaWrapper(this: PluginContext): void { + mirrorArkts.arktsGlobal.config = arkts.arktsGlobal.config; + const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr() + mirrorArkts.arktsGlobal.compilerContext = new mirrorArkts.Context(contextPtr) + console.log('[MIRROR]: copied config and context from koala-wrapper') +} \ No newline at end of file diff --git a/arkui-plugins/mirror-replace/common/debug.ts b/arkui-plugins/mirror-replace/common/debug.ts new file mode 100644 index 0000000000000000000000000000000000000000..828e3e71d7e58e31553f7890eab307265cf413ee --- /dev/null +++ b/arkui-plugins/mirror-replace/common/debug.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 fs from 'fs'; +import * as path from 'path'; +import * as mirrorArkts from '@koalaui/libarkts-mirror'; + +const isDebugLog: boolean = true; +const isDebugDump: boolean = false; +const isPerformance: boolean = false; +const enableMemoryTracker: boolean = false; +// mirrorArkts.Performance.getInstance().skip(!isPerformance); +// mirrorArkts.Performance.getInstance().enableMemoryTracker(enableMemoryTracker); +export function getEnumName(enumType: any, value: number): string | undefined { + return enumType[value]; +} + +function mkDir(filePath: string): void { + const parent = path.join(filePath, '..'); + if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { + mkDir(parent); + } + fs.mkdirSync(filePath); +} + +export function debugDump( + content: string, + fileName: string, + isInit: boolean, + cachePath: string | undefined, + programFileName: string +): void { + if (!isDebugDump) return; + const currentDirectory = process.cwd(); + const modifiedFileName = programFileName.replaceAll('.', '_'); + const outputDir: string = cachePath + ? path.resolve(currentDirectory, cachePath, modifiedFileName) + : path.resolve(currentDirectory, 'dist', 'cache', modifiedFileName); + const filePath: string = path.resolve(outputDir, fileName); + if (!fs.existsSync(outputDir)) { + mkDir(outputDir); + } + try { + if (!isInit && fs.existsSync(filePath)) { + const existingContent = fs.readFileSync(filePath, 'utf8'); + const newContent = + existingContent && !existingContent.endsWith('\n') + ? existingContent + '\n' + content + : existingContent + content; + fs.writeFileSync(filePath, newContent, 'utf8'); + } else { + fs.writeFileSync(filePath, content, 'utf8'); + } + } catch (error) { + console.error('文件操作失败:', error); + } +} + +export function debugLog(message?: any, ...optionalParams: any[]): void { + if (!isDebugLog) return; + console.log(message, ...optionalParams); +} + +export function getDumpFileName(state: number, prefix: string, index: number | undefined, suffix: string): string { + return `${state}_${prefix}_${index ?? ''}_${suffix}.sts`; +} diff --git a/arkui-plugins/mirror-replace/common/plugin-context.ts b/arkui-plugins/mirror-replace/common/plugin-context.ts new file mode 100644 index 0000000000000000000000000000000000000000..3621ff89105d0f120d7fe27f44a45025557b77e2 --- /dev/null +++ b/arkui-plugins/mirror-replace/common/plugin-context.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 mirrorArkts from '@koalaui/libarkts-mirror'; + +// This is the same plugin-context in the build-system. +export class PluginContext { + private projectConfig: ProjectConfig | undefined; + private contextPtr: number | undefined; + + constructor() { + this.projectConfig = undefined; + this.contextPtr = undefined; + } + + public setProjectConfig(projectConfig: ProjectConfig): void { + this.projectConfig = projectConfig; + } + + public getProjectConfig(): ProjectConfig | undefined { + return this.projectConfig; + } + + public setContextPtr(ptr: number): void { + this.contextPtr = ptr; + } + + public getContextPtr(): number | undefined { + return this.contextPtr; + } +} + +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; +} + +export type PluginHandlerFunction = () => void; + +export type PluginHandlerObject = { + order: 'pre' | 'post' | undefined; + handler: PluginHandlerFunction; +}; + +export type PluginHandler = PluginHandlerFunction | PluginHandlerObject; + +export interface Plugins { + name: string; + afterNew?: PluginHandler; + parsed?: PluginHandler; + scopeInited?: PluginHandler; + checked?: PluginHandler; + lowered?: PluginHandler; + asmGenerated?: PluginHandler; + binGenerated?: PluginHandler; + clean?: PluginHandler; +} + +export type PluginState = keyof Omit; + +export type PluginExecutor = { + name: string; + handler: PluginHandlerFunction; +}; diff --git a/arkui-plugins/mirror-replace/common/predefines.ts b/arkui-plugins/mirror-replace/common/predefines.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e51e18f7f1eba1e916fb68d3240cdc7d7cfe67d --- /dev/null +++ b/arkui-plugins/mirror-replace/common/predefines.ts @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 EXTERNAL_SOURCE_PREFIX_NAMES: (string | RegExp)[] = [ + 'std', + 'escompat', + 'security', + 'application', + 'permissions', + 'bundleManager', + 'commonEvent', + /@arkts\..*/, + /@ohos\.(?!arkui).*/, + /@system\..*/, + /arkui\.(?!Ark|[Uu]serView$)[A-Z]/, // temporary solution + /ability\..*/, +]; + +export const ARKUI_IMPORT_PREFIX_NAMES: (string | RegExp)[] = [/arkui\..*/, /@ohos\..*/, /@kit\..*/]; + +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 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 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 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_DECORATED = 'IStoragePropDecoratedVariable', + PROP_DECORATED = 'IPropDecoratedVariable', + MUTABLE_STATE = 'MutableState', + 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 = 'makeStorageProp', + MAKE_STORAGE_LINK = 'makeStorageLink', + 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 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_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP, StateManagementTypes.SYNCED_PROPERTY], + [DecoratorNames.LOCAL_STORAGE_LINK, StateManagementTypes.MUTABLE_STATE], + [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_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.STORAGE_LINK_STATE, + StateManagementTypes.MUTABLE_STATE, + StateManagementTypes.OBSERVABLE_PROXY, + ], + ], + [ + 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]] +]); + +/** + * @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.MUTABLE_STATE, 'arkui.stateManagement.runtime'], + [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', +} \ No newline at end of file diff --git a/arkui-plugins/mirror-replace/common/program-visitor.ts b/arkui-plugins/mirror-replace/common/program-visitor.ts new file mode 100644 index 0000000000000000000000000000000000000000..6838f5f249dea4b53b8b44dbfe1db71c9c85998e --- /dev/null +++ b/arkui-plugins/mirror-replace/common/program-visitor.ts @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as mirrorArkts from '@koalaui/libarkts-mirror'; +import { AbstractVisitor, VisitorOptions } from './abstract-visitor'; +import { matchPrefix } from './arkts-utils'; +import { debugDump, getDumpFileName } from './debug'; +import { PluginContext } from './plugin-context'; +import { InteroperAbilityNames } from '../../ui-plugins/interop/predefines'; +// import { LegacyTransformer } from '../ui-plugins/interop/legacy-transformer'; +// import { ComponentTransformer } from '../ui-plugins/component-transformer'; + +export interface ProgramVisitorOptions extends VisitorOptions { + pluginName: string; + state: mirrorArkts.Es2pandaContextState; + visitors: AbstractVisitor[]; + skipPrefixNames: (string | RegExp)[]; + hooks?: ProgramHooks; + pluginContext?: PluginContext; +} + +export interface ProgramHookConfig { + visitors: AbstractVisitor[]; + resetAfter?: mirrorArkts.Es2pandaContextState; +} + +export type ProgramHookLifeCycle = Partial>; + +export interface ProgramHooks { + external?: ProgramHookLifeCycle; + source?: ProgramHookLifeCycle; +} + +function flattenVisitorsInHooks( + programHooks?: ProgramHooks, + resetAfterValue?: mirrorArkts.Es2pandaContextState +): AbstractVisitor[] { + if (!programHooks) return []; + const flatMapInHook = (config: ProgramHookConfig): AbstractVisitor[] => { + if (!resetAfterValue) return []; + if (!config.resetAfter || resetAfterValue !== config.resetAfter) return []; + return config.visitors; + }; + return [ + ...Object.values(programHooks.external || {}).flatMap(flatMapInHook), + ...Object.values(programHooks.source || {}).flatMap(flatMapInHook), + ]; +} + +export interface StructMap { + [key: string]: string; +} + +export class ProgramVisitor extends AbstractVisitor { + private readonly pluginName: string; + private readonly state: mirrorArkts.Es2pandaContextState; + private readonly visitors: AbstractVisitor[]; + private readonly skipPrefixNames: (string | RegExp)[]; + private readonly hooks?: ProgramHooks; + private filenames: Map; + private pluginContext?: PluginContext; + private legacyModuleList: string[] = []; + private legacyStructMap: Map; + + constructor(options: ProgramVisitorOptions) { + super(options); + this.pluginName = options.pluginName; + this.state = options.state; + this.visitors = options.visitors; + this.skipPrefixNames = options.skipPrefixNames ?? []; + 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 = []; + } + + 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); + } + } + } + + private dumpExternalSource( + script: mirrorArkts.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: mirrorArkts.Program, name: string): void { + // if (this.state === mirrorArkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { + // const structList = this.visitorLegacy(currProgram.ast, currProgram, name); + // const moduleName = name.split('/')[0]; + // const structMap = this.legacyStructMap.get(moduleName)!; + // for (const struct of structList) { + // structMap[struct] = name; + // } + // } + // } + + private visitNonLegacyInExternalSource( + program: mirrorArkts.Program, + currProgram: mirrorArkts.Program, + name: string, + cachePath?: string + ): void { + const extensionName: string = program.fileNameWithExtension; + this.dumpExternalSource(currProgram.ast, name, cachePath, 'ORI', extensionName); + const script = this.visitor(currProgram.ast, currProgram, name); + if (script) { + this.dumpExternalSource(script, name, cachePath, this.pluginName, extensionName); + } + } + + private visitNextProgramInQueue( + queue: mirrorArkts.Program[], + visited: Set, + externalSource: mirrorArkts.ExternalSource + ): void { + const nextProgramArr: mirrorArkts.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: mirrorArkts.Program, + programQueue: mirrorArkts.Program[] + ): void { + const visited = new Set(); + const queue: mirrorArkts.Program[] = programQueue; + this.getLegacyModule(); + while (queue.length > 0) { + const currProgram = queue.shift()!; + if (visited.has(currProgram.peer) || currProgram.isASTLowered) { + continue; + } + if (currProgram.peer !== program.peer) { + const name: string = this.filenames.get(currProgram.peer)!; + const cachePath: string | undefined = this.pluginContext?.getProjectConfig()?.cachePath; + if (this.legacyModuleList && matchPrefix(this.legacyModuleList, name)) { + // this.visitLegacyInExternalSource(currProgram, name); + } else { + this.visitNonLegacyInExternalSource(program, currProgram, name, cachePath); + } + } + visited.add(currProgram.peer); + for (const externalSource of mirrorArkts.programGetExternalSources(currProgram)) { + if (matchPrefix(this.skipPrefixNames, externalSource.getName())) { + continue; + } + this.visitNextProgramInQueue(queue, visited, externalSource); + } + } + } + + programVisitor(program: mirrorArkts.Program): mirrorArkts.Program { + this.visitExternalSources(program, [program]); + let programScript = program.ast; + programScript = this.visitor(programScript, program, this.externalSourceName); + const visitorsToReset = flattenVisitorsInHooks(this.hooks, this.state); + visitorsToReset.forEach((visitor) => visitor.reset()); + + return program; + } + + private preVisitor( + hook: ProgramHookLifeCycle | undefined, + node: mirrorArkts.AstNode, + program?: mirrorArkts.Program, + externalSourceName?: string + ): void { + let script: mirrorArkts.ETSModule = node as mirrorArkts.ETSModule; + 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: mirrorArkts.AstNode, + program?: mirrorArkts.Program, + externalSourceName?: string + ): void { + let script: mirrorArkts.ETSModule = node as mirrorArkts.ETSModule; + 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: mirrorArkts.AstNode, program?: mirrorArkts.Program, externalSourceName?: string): mirrorArkts.ETSModule { + let hook: ProgramHookLifeCycle | undefined; + + let script: mirrorArkts.ETSModule = node as mirrorArkts.ETSModule; + let count: number = 0; + const isExternal: boolean = !!externalSourceName; + + // pre-run visitors + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.preVisitor(hook, node, program, externalSourceName); + + for (const transformer of this.visitors) { + // if (this.legacyStructMap.size > 0 && transformer instanceof ComponentTransformer) { + // transformer.registerMap(this.legacyStructMap); + // } + this.visitTransformer(transformer, script, externalSourceName, program); + transformer.reset(); + mirrorArkts.setAllParents(script); + if (!transformer.isExternal) { + debugDump( + script.dumpSrc(), + getDumpFileName(this.state, this.pluginName, count, transformer.constructor.name), + true, + this.pluginContext?.getProjectConfig()?.cachePath, + program!.fileNameWithExtension + ); + count += 1; + } + } + + // post-run visitors + hook = isExternal ? this.hooks?.external : this.hooks?.source; + this.postVisitor(hook, node, program, externalSourceName); + return script; + } + + // private visitorLegacy(node: mirrorArkts.AstNode, program?: mirrorArkts.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: mirrorArkts.ETSModule, + externalSourceName?: string, + program?: mirrorArkts.Program + ): mirrorArkts.ETSModule { + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + const newScript = transformer.visitor(script) as mirrorArkts.ETSModule; + program?.ast.setStatements(newScript.statements) + return newScript; + } +} diff --git a/arkui-plugins/mirror-replace/interop-plugins/arkuiImportList.ts b/arkui-plugins/mirror-replace/interop-plugins/arkuiImportList.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddc19b2bd98bf3d005f43c654945be7fbc640c2f --- /dev/null +++ b/arkui-plugins/mirror-replace/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/mirror-replace/interop-plugins/decl_transformer.ts b/arkui-plugins/mirror-replace/interop-plugins/decl_transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ec5a9b05b24f11c2f16a3c32ff8d9a9778bd81e --- /dev/null +++ b/arkui-plugins/mirror-replace/interop-plugins/decl_transformer.ts @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as mirrorArkts from '@koalaui/libarkts-mirror'; + +import { AbstractVisitor } from '../common/abstract-visitor'; +import { ARKUI_DECLARE_LIST } from './arkuiImportList'; +import { debugLog } from '../common/debug'; + +export class DeclTransformer extends AbstractVisitor { + constructor(private options?: interop.DeclTransformerOptions) { + super(); + } + + processComponent(node: mirrorArkts.ETSStructDeclaration): mirrorArkts.ClassDeclaration { + const className = node.definition?.ident?.name; + if (!className) { + throw 'Non Empty className expected for Component'; + } + + let newDec: mirrorArkts.ClassDeclaration = mirrorArkts.factory.createClassDeclaration(node.definition); + + const newDefinition = mirrorArkts.factory.updateClassDefinition( + newDec.definition!, + newDec.definition?.ident, + undefined, + undefined, + newDec.definition?.implements!, + undefined, + undefined, + node.definition?.body, + newDec.definition?.modifiers!, + mirrorArkts.classDefinitionFlags(newDec.definition!) | mirrorArkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + + mirrorArkts.factory.updateClassDeclaration(newDec, newDefinition); + newDec.modifierFlags = node.modifierFlags; + return newDec; + } + + visitor(beforeChildren: mirrorArkts.AstNode): mirrorArkts.AstNode { + let astNode: mirrorArkts.AstNode = beforeChildren; + if (astNode instanceof mirrorArkts.ETSModule) { + astNode = this.transformImportDecl(astNode); + } + const node = this.visitEachChild(astNode); + if (mirrorArkts.isETSStructDeclaration(node)) { + debugLog(`[MIRROR]: DeclTransformer:before:flag:${node.definition!.isFromStruct}`); + node.definition!.setFromStructModifier(); + let newnode = this.processComponent(node); + debugLog(`[MIRROR]: DeclTransformer:after:flag:${newnode.definition!.isFromStruct}`); + return newnode; + } + else if (mirrorArkts.isETSImportDeclaration(astNode)) { + return this.updateImportDeclaration(astNode); + } + else if (mirrorArkts.isMethodDefinition(astNode)) { + if (astNode.id?.name === 'build') { + return this.transformMethodDefinition(astNode); + } + return astNode; + } + return node; + } + + transformImportDecl(astNode: mirrorArkts.AstNode): mirrorArkts.AstNode { + if (!(astNode instanceof mirrorArkts.ETSModule)) { + return astNode; + } + let statements = astNode.statements.filter(node => this.isImportDeclarationNeedFilter(node)); + return mirrorArkts.factory.updateETSModule(astNode, statements, astNode.ident, astNode.getNamespaceFlag(), astNode.program); + } + + transformMethodDefinition(node: mirrorArkts.MethodDefinition): mirrorArkts.AstNode { + const func: mirrorArkts.ScriptFunction = node.function!; + const updateFunc = mirrorArkts.factory.updateScriptFunction( + func, + !!func.body && mirrorArkts.isBlockStatement(func.body) + ? mirrorArkts.factory.updateBlockStatement( + func.body, + func.body.statements.filter(() => false) + ) + : undefined, + func.typeParams, func.params, func.returnTypeAnnotation, false, + func?.flags, + func?.modifierFlags, + func.id, + func.annotations + ); + + return mirrorArkts.factory.updateMethodDefinition( + node, + node.kind, + mirrorArkts.factory.updateIdentifier( + node.id!, + node.id?.name! + ), + mirrorArkts.factory.createFunctionExpression(updateFunc.id, updateFunc), + node.modifierFlags, + false, + node.overloads + ); + } + + isImportDeclarationNeedFilter(astNode: mirrorArkts.AstNode): boolean { + if (!mirrorArkts.isETSImportDeclaration(astNode)) { + return true; + } + return astNode?.source?.str !== '@global.arkui'; + } + + updateImportDeclaration(astNode: mirrorArkts.AstNode): mirrorArkts.AstNode { + if (!mirrorArkts.isETSImportDeclaration(astNode) || astNode?.source?.str !== '@ohos.arkui.component') { + return astNode; + } + astNode.specifiers.forEach((element) => { + if (mirrorArkts.isImportSpecifier(element)) { + if (ARKUI_DECLARE_LIST.has(element.imported?.name as string)) { + element.setRemovable(true); + } + } + }); + return astNode; + } +} diff --git a/arkui-plugins/mirror-replace/interop-plugins/emit_transformer.ts b/arkui-plugins/mirror-replace/interop-plugins/emit_transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd9d18ac9ab818038cc452b387222d4a8c06d7d6 --- /dev/null +++ b/arkui-plugins/mirror-replace/interop-plugins/emit_transformer.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 * as mirrorArkts from '@koalaui/libarkts-mirror'; + +import { AbstractVisitor } from '../common/abstract-visitor'; + +import { debugLog } from '../common/debug'; + +export class EmitTransformer extends AbstractVisitor { + constructor(private options?: interop.EmitTransformerOptions) { + super(); + } + + processComponent(node: mirrorArkts.ClassDeclaration): mirrorArkts.ClassDeclaration { + const className = node.definition?.ident?.name; + if (!className) { + throw 'Non Empty className expected for Component'; + } + + const newDefinition = mirrorArkts.factory.updateClassDefinition( + node.definition, + node.definition?.ident, + undefined, + undefined, + node.definition?.implements, + undefined, + undefined, + node.definition?.body, + node.definition?.modifiers, + mirrorArkts.classDefinitionFlags(node.definition) | mirrorArkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ); + + let newDec: mirrorArkts.ClassDeclaration = mirrorArkts.factory.updateClassDeclaration(node, newDefinition); + + debugLog(`[MIRROR]: DeclTransformer:checked:struct_ast:${newDefinition.dumpJson()}`); + newDec.modifierFlags = node.modifierFlags; + return newDec; + } + + visitor(beforeChildren: mirrorArkts.AstNode): mirrorArkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (mirrorArkts.isClassDeclaration(node) && node.definition!.isFromStruct) { + return this.processComponent(node); + } + return node; + } +} diff --git a/arkui-plugins/mirror-replace/interop-plugins/index.ts b/arkui-plugins/mirror-replace/interop-plugins/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a54d9eec18d19f179411391ae9db28b4294119fc --- /dev/null +++ b/arkui-plugins/mirror-replace/interop-plugins/index.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as mirrorArkts from '@koalaui/libarkts-mirror'; + +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'; +import { copyGlobalFromKoalaWrapper } from '../common/compat-koala-wrapper' + +export function interopTransform(): Plugins { + return { + name: 'interop-plugin', + parsed: parsedTransform, + checked: checkedTransform, + }; +} + +function parsedTransform(this: PluginContext): mirrorArkts.ETSModule | undefined { + copyGlobalFromKoalaWrapper(this); + + let script: mirrorArkts.ETSModule | undefined; + debugLog('[MIRROR]: interopTransform:parsed'); + const contextPtr = mirrorArkts.arktsGlobal.context; + if (!!contextPtr) { + let program = mirrorArkts.arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.ast as mirrorArkts.ETSModule; + if (script) { + debugLog(`[MIRROR]: interopTransform: script before parsing: ${script?.dumpSrc()}`) + const declTransformer = new DeclTransformer({ + arkui: '@koalaui.arkts-arkui.StructParse' as interop.TransfromerName + }); + + const programVisitor = new ProgramVisitor({ + pluginName: interopTransform().name, + state: mirrorArkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, + visitors: [declTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this as unknown as PluginContext + }); + program = programVisitor.programVisitor(program); + script = program.ast as mirrorArkts.ETSModule; + debugLog(`[MIRROR]: interopTransform: script after parsing: ${script?.dumpSrc()}`) + debugLog('[MIRROR]: interopTransform: parsed exit'); + return script; + } + } + debugLog('[MIRROR]: interopTransform: parsed exit with no transform'); + return script; +} + +function checkedTransform(this: PluginContext): mirrorArkts.ETSModule | undefined { + copyGlobalFromKoalaWrapper(this); + + let script: mirrorArkts.ETSModule | undefined; + debugLog('[MIRROR]: interopTransform:checked'); + const contextPtr = mirrorArkts.arktsGlobal.context; + if (!!contextPtr) { + let program = mirrorArkts.arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.ast as mirrorArkts.ETSModule; + if (script) { + debugLog(`[MIRROR]: interopTransform: script before checking: ${script?.dumpSrc()}`) + const emitTransformer = new EmitTransformer({ + arkui: '@koalaui.arkts-arkui.EmitBase' as interop.TransfromerName + }); + + const programVisitor = new ProgramVisitor({ + pluginName: interopTransform().name, + state: mirrorArkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [emitTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this as unknown as PluginContext + }); + program = programVisitor.programVisitor(program); + script = program.ast as mirrorArkts.ETSModule + mirrorArkts.recheckSubtree(script); + debugLog(`[MIRROR]: interopTransform: script after checking: ${script?.dumpSrc()}`) + debugLog('[MIRROR]: interopTransform:checked exit'); + return script; + } + } + debugLog('[MIRROR]: interopTransform:checked exit with no transform'); + return script; +} diff --git a/arkui-plugins/mirror-replace/interop-plugins/types.ts b/arkui-plugins/mirror-replace/interop-plugins/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..4bb425a4b72ba96879c0ec99e1459171af6f1f9d --- /dev/null +++ b/arkui-plugins/mirror-replace/interop-plugins/types.ts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace interop { + export type NativePointer = number; + + export interface PluginContext { + setArkTSAst(ast: Node): void; + getArkTSAst(): Node | undefined; + setArkTSProgram(program: Node): void; + getArkTSProgram(): Node | undefined; + setProjectConfig(projectConfig: Node): void; + getProjectConfig(): Node | undefined; + } + + export interface ArktsObject { + readonly peer: NativePointer; + } + + export interface Node extends ArktsObject { + get originalPeer(): NativePointer; + set originalPeer(peer: NativePointer); + dumpJson(): string; + dumpSrc(): string; + } + + export interface ETSModule extends Node { } + + export interface Plugin { + name: string; + parsed?(context: PluginContext): ETSModule | undefined; + checked?(context: PluginContext): ETSModule | undefined; + } + + export type TransfromerName = string & { __TransfromerNameBrand: any }; + + export interface EmitTransformerOptions { + arkui: TransfromerName; + } + + export interface DeclTransformerOptions { + arkui: TransfromerName; + } +} diff --git a/arkui-plugins/package.json b/arkui-plugins/package.json index 929c12377e1d5259d15b24c986a6e41ff93b5e70..47ca21127b06684154544e5703143cbbd5f086f9 100644 --- a/arkui-plugins/package.json +++ b/arkui-plugins/package.json @@ -31,6 +31,7 @@ "typescript": "^5.0.0" }, "dependencies": { - "@koalaui/libarkts": "../koala-wrapper" + "@koalaui/libarkts": "../koala-wrapper", + "@koalaui/libarkts-mirror": "../koala-wrapper/mirror" } } diff --git a/arkui-plugins/path.ts b/arkui-plugins/path.ts index a8646fcbf9f1a0b882082eca9843bd5441c171d9..336b17831c3c3e19e59f0e50483ef214ef6f9ac7 100644 --- a/arkui-plugins/path.ts +++ b/arkui-plugins/path.ts @@ -59,3 +59,7 @@ export function getCommonPath() { export function getCompatPath() { return path.join(findRootDir(), 'koala-wrapper/koalaui/compat', './dist/src/index.js'); } + +export function getArktsMirrorPath() { + return path.join(findRootDir(), 'koala-wrapper/mirror', './build/libarkts.js'); +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/build_decl_config_template.json b/arkui-plugins/test/demo/localtest/build_decl_config_template.json index fc58a0e00cabfba924d9cb7f873a3b14efd0358d..b546d47730354889e73bc5987e710df183fa7a6f 100644 --- a/arkui-plugins/test/demo/localtest/build_decl_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_decl_config_template.json @@ -4,7 +4,7 @@ }, "compileFiles": [ - "./demo/localtest/entry/new.ets" + "./demo/localtest/entry/src/main/ets/pages/new.ets" ], "packageName" : "entry", diff --git a/arkui-plugins/test/mirror/demo/entry/src/main/ets/pages/new.ets b/arkui-plugins/test/mirror/demo/entry/src/main/ets/pages/new.ets new file mode 100755 index 0000000000000000000000000000000000000000..b3627f723145bdfcf148575dad43a3e2726d4e8f --- /dev/null +++ b/arkui-plugins/test/mirror/demo/entry/src/main/ets/pages/new.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 { 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 +export struct MyStateSample { + @State stateVar: string = "state var"; + message: string = `click to change state variable, add **`; + changeValue() { + this.stateVar+="**" + } + build() { + 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() + }) + Text(this.stateVar).fontSize(20) + Child({linkVar: this.stateVar, propVar: this.stateVar}) + }.margin(10) + } +} + +@Component +export struct Child { + @Link linkVar: string = ""; // TODO: remove this + @Prop propVar: string = "Prop"; + + changeValue1() { + this.linkVar+="!!" + } + + changeValue2() { + this.propVar+="~~" + } + + 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) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/mirror/mirror_decl_config_template.json b/arkui-plugins/test/mirror/mirror_decl_config_template.json new file mode 100644 index 0000000000000000000000000000000000000000..5beb6168c8f501725397c01b1750d99d6d888e2d --- /dev/null +++ b/arkui-plugins/test/mirror/mirror_decl_config_template.json @@ -0,0 +1,29 @@ +{ + "plugins": { + "interop_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/mirror-replace/interop-plugins/index" + }, + + "compileFiles": [ + "./mirror/demo/entry/src/main/ets/pages/new.ets" + ], + + "packageName" : "entry", + + "buildType": "build", + "buildMode": "Debug", + "moduleRootPath": "./mirror/demo/entry/", + "sourceRoots": ["./"], + + "loaderOutPath": "./dist", + "cachePath": "./dist/cache", + + "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", + + "dependentModuleList": [], + + "isIDE": "false", + "enableDeclgenEts2Ts": true, + + "declgenV1OutPath": "workspace/developtools/ace_ets2bundle/arkui-plugins/test/mirror/demo/hello_world/declgenV1OutPath", + "declgenBridgeCodePath": "workspace/developtools/ace_ets2bundle/arkui-plugins/test/mirror/demo/hello_world/declgenBridgeCodePath" +} diff --git a/arkui-plugins/test/mirror_decl_config.js b/arkui-plugins/test/mirror_decl_config.js new file mode 100644 index 0000000000000000000000000000000000000000..954951f940ee4e6963cbb1ca25a4d90eb5845036 --- /dev/null +++ b/arkui-plugins/test/mirror_decl_config.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const path = require('path'); + +const currentDirectory = process.cwd(); +let workSpace = currentDirectory; +for (let i = 0; i < 4; i++) { + workSpace = path.dirname(workSpace); +} + +const jsonFilePath = path.join(__dirname, 'mirror/mirror_decl_config_template.json'); +const outJsonFilePath = path.join(__dirname, 'mirror/mirror_decl_config.json'); + +try { + const data = fs.readFileSync(jsonFilePath, 'utf8'); + const jsonData = JSON.parse(data); + + if (jsonData.buildSdkPath) { + jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); + } + + if (jsonData.plugins.interop_plugin) { + jsonData.plugins.interop_plugin = jsonData.plugins.interop_plugin.replace(/workspace/g, workSpace); + } + + if (jsonData.declgenV1OutPath) { + jsonData.declgenV1OutPath = jsonData.declgenV1OutPath.replace(/workspace/g, workSpace); + } + + if (jsonData.declgenBridgeCodePath) { + jsonData.declgenBridgeCodePath = jsonData.declgenBridgeCodePath.replace(/workspace/g, workSpace); + } + + fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); +} catch (error) { + console.error('writeFile error:', error); +} diff --git a/arkui-plugins/test/package.json b/arkui-plugins/test/package.json index 46947c865d37f49fcd57c1804898a9d9a0288558..abdbcd3e92adba8b05e371ca75044480b5112ed6 100644 --- a/arkui-plugins/test/package.json +++ b/arkui-plugins/test/package.json @@ -20,9 +20,10 @@ "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", + "localtest_decl": "rm -rf demo/hello_world/ && 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", "localtest_all": "npm run localtest_decl && npm run localtest", "es2panda:compile": "node ./arktsconfig_gen.js && $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/bin/es2panda --arktsconfig=./dist/cache/arktsconfig.json", - "es2panda:test": "$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/bin/es2panda ./demo/localtest/entry/test.ets --output=test.abc" + "es2panda:test": "$INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/bin/es2panda ./demo/localtest/entry/test.ets --output=test.abc", + "mirrortest:decl": "rm -rf mirror/demo/hello_world/ && node mirror_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 ./mirror/mirror_decl_config.json" } } diff --git a/arkui-plugins/test_koala_mirror.py b/arkui-plugins/test_koala_mirror.py new file mode 100755 index 0000000000000000000000000000000000000000..31ff9be73420989c388cca70776de6d4c0ddb466 --- /dev/null +++ b/arkui-plugins/test_koala_mirror.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess +import argparse +import sys + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--npm', help='path to a npm exetuable') + parser.add_argument('--test_path', help='current_os') + + options = parser.parse_args() + return options + +def run_cmd(cmd, execution_path=None): + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=execution_path) + stdout, stderr = proc.communicate(timeout=1000) + if proc.returncode != 0: + raise Exception(stderr.decode()) + +EXPECTED_DECL_RESULT=""" +import { State, Link, Prop } from "@ohos.arkui.stateManagement"; +@Component +export declare struct MyStateSample { + @State + stateVar: string; + message: string; + public changeValue(): void; + public build(): void; +} +@Component +export declare struct Child { + @Link + linkVar: string; + @Prop + propVar: string; + public changeValue1(): void; + public changeValue2(): void; + public build(): void; +} +""" + +def main(): + options = parse_args() + run_cmd([options.npm, 'run', 'mirrortest:decl'], options.test_path) + decl_test_file_path = f"{options.test_path}/mirror/demo/hello_world/declgenV1OutPath/entry/src/main/ets/pages/new.d.ets" + with open(decl_test_file_path, 'r', encoding='utf-8') as f: + content = f.read() + print(content) + if content.strip() != EXPECTED_DECL_RESULT.strip(): + raise Exception("Error: failed to pass koala-mirror test") + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/koala-wrapper/BUILD.gn b/koala-wrapper/BUILD.gn index 58807818d720d8eb681392d0927e90d37e035192..c7363952fb7f76b1a8c4a4db86394cafe68775de 100644 --- a/koala-wrapper/BUILD.gn +++ b/koala-wrapper/BUILD.gn @@ -15,10 +15,12 @@ import("//build/ohos.gni") import("//build/config/components/ets_frontend/ets2abc_config.gni") npm_path = "//prebuilts/build-tools/common/nodejs/current/bin/npm" +mirror_ui2abc_dir = "//foundation/arkui/ace_engine/frameworks/bridge/arkts_frontend/koala_mirror/ui2abc" action("gen_sdk_ts_wrapper") { script = "build_ts_wrapper.py" deps = [ "./native:es2panda" ] + external_deps = [ "ace_engine:libarkts" ] args = [ "--source_path", rebase_path(get_path_info(".", "abspath")), @@ -30,6 +32,8 @@ action("gen_sdk_ts_wrapper") { "$current_os", "--root_out_dir", rebase_path(root_out_dir), + "--mirror_ui2abc_dir", + rebase_path(mirror_ui2abc_dir) ] outputs = [ "$target_gen_dir" ] diff --git a/koala-wrapper/build_ts_wrapper.py b/koala-wrapper/build_ts_wrapper.py index 178f8772f1db122e5203cc1227c9cfad48f4ba31..649a5a48b6a1478485f5ce597075ad5b2191b343 100755 --- a/koala-wrapper/build_ts_wrapper.py +++ b/koala-wrapper/build_ts_wrapper.py @@ -18,8 +18,6 @@ import os import shutil import subprocess import sys -import tarfile - def copy_files(source_path, dest_path, is_file=False): try: @@ -59,17 +57,27 @@ def copy_output(options): copy_files(os.path.join(options.source_path, 'package.json'), os.path.join(options.output_path, 'package.json'), True) + copy_files(os.path.join(options.mirror_ui2abc_dir, 'libarkts/lib/libarkts.js'), + os.path.join(options.output_path, 'mirror/build/libarkts.js'), True) + if options.current_os == "mingw" : copy_files(os.path.join(options.root_out_dir, 'libes2panda.dll'), os.path.join(options.output_path, 'build/native/es2panda.node'), True) - copy_files(os.path.join(options.root_out_dir, 'libes2panda.dll'), - os.path.join(options.source_path, 'build/native/es2panda.node'), True) + + copy_files(os.path.join(options.root_out_dir, 'libes2panda_lib.dll'), + os.path.join(options.output_path, 'mirror/build/native/build/es2panda.node'), True) if options.current_os == "linux" or options.current_os == "mac" : + run_cmd(['rm', '-rf', os.path.join(options.source_path, 'mirror/build')]) + + copy_files(os.path.join(options.mirror_ui2abc_dir, 'libarkts/lib/build'), + os.path.join(options.source_path, 'mirror/build')) + copy_files(os.path.join(options.root_out_dir, 'libes2panda.node'), os.path.join(options.output_path, 'build/native/es2panda.node'), True) - copy_files(os.path.join(options.root_out_dir, 'libes2panda.node'), - os.path.join(options.source_path, 'build/native/es2panda.node'), True) + + copy_files(os.path.join(options.root_out_dir, 'libes2panda_lib.node'), + os.path.join(options.output_path, 'mirror/build/native/build/es2panda.node'), True) def parse_args(): @@ -79,6 +87,7 @@ def parse_args(): parser.add_argument('--output_path', help='path to output') parser.add_argument('--root_out_dir', help='path to root out') parser.add_argument('--current_os', help='current_os') + parser.add_argument('--mirror_ui2abc_dir', help='mirror_ui2abc_dir') options = parser.parse_args() return options diff --git a/koala-wrapper/mirror/package.json b/koala-wrapper/mirror/package.json new file mode 100644 index 0000000000000000000000000000000000000000..06ca6f7749d455497ecf1d1fed4a336a2bb87b26 --- /dev/null +++ b/koala-wrapper/mirror/package.json @@ -0,0 +1,10 @@ +{ + "name": "@koalaui/libarkts-mirror", + "version": "1.0.0", + "private": true, + "main": "./build/index.js", + "types": "./build/index.d.ts", + "exports": { + ".": "./build/index.js" + } +} diff --git a/koala-wrapper/src/Es2pandaEnums.ts b/koala-wrapper/src/Es2pandaEnums.ts index 9bb06c7d6109ad32fa7b369f52a043114292b7f5..7c287f65ae5ae7cc2a086b9df85e98b77ce97154 100644 --- a/koala-wrapper/src/Es2pandaEnums.ts +++ b/koala-wrapper/src/Es2pandaEnums.ts @@ -174,7 +174,7 @@ export enum Es2pandaAstNodeType { AST_NODE_TYPE_YIELD_EXPRESSION, AST_NODE_TYPE_OPAQUE_TYPE_NODE, AST_NODE_TYPE_BLOCK_EXPRESSION, - AST_NODE_TYPE_ERROR_TYPE_NODE, + AST_NODE_TYPE_BROKEN_TYPE_NODE, AST_NODE_TYPE_ARRAY_EXPRESSION, AST_NODE_TYPE_ARRAY_PATTERN, AST_NODE_TYPE_ASSIGNMENT_EXPRESSION,