diff --git a/arkui-plugins/common/metadata-collector.ts b/arkui-plugins/common/metadata-collector.ts new file mode 100644 index 0000000000000000000000000000000000000000..c9f9dd5443b94da22eb5170c795ae7d2289eea87 --- /dev/null +++ b/arkui-plugins/common/metadata-collector.ts @@ -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 { ProjectConfig } from './plugin-context'; + +export class MetaDataCollector { + public projectConfig: ProjectConfig | undefined; + public fileAbsName: string | undefined; + private static instance: MetaDataCollector; + + static getInstance(): MetaDataCollector { + if (!this.instance) { + this.instance = new MetaDataCollector(); + } + return this.instance; + } + + setProjectConfig(config: ProjectConfig | undefined): this { + this.projectConfig = config; + return this; + } + + setAbsName(fileName: string | undefined) { + this.fileAbsName = fileName; + return this; + } + + reset(): void { + this.projectConfig = undefined; + this.fileAbsName = undefined; + } +} \ No newline at end of file diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index db95432b858919afb42776ef74cdfd05635ce867..9e7475af8a7abc0b52b751839f0cd854da9dd9c6 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -42,6 +42,9 @@ export const ENTRY_POINT_IMPORT_SOURCE_NAME: string = 'arkui.UserView'; export const ARKUI_COMPONENT_COMMON_SOURCE_NAME: string = 'arkui.component.common'; export const ARKUI_FOREACH_SOURCE_NAME: string = 'arkui.component.forEach'; export const ARKUI_BUILDER_SOURCE_NAME: string = 'arkui.component.builder'; +export const ARKUI_COMPONENT_NAVIGATION = 'arkui.component.navigation'; +export const ARKUI_COMPONENT_NAVDESTINATION = 'arkui.component.navDestination'; +export const ARKUI_COMPONENT_BUILDER = 'arkui.component.builder'; export enum ModuleType { HAR = 'har', @@ -246,6 +249,17 @@ export enum NavigationNames { PAGE_PATH = 'pagePath', PAGE_FULL_PATH = 'pageFullPath', INTEGRATED_HSP = 'integratedHsp', + NAVIGATION = 'Navigation', + NAVIGATION_MODULE_INFO = 'NavigationModuleInfo', + IS_USER_CREATE_STATCK = 'isUserCreateStack', + MODULE_INFO = 'moduleInfo', + NAVDESTINATION = 'NavDestination', + NAVIGATION_BUILDER_REGISTER = 'NavigationBuilderRegister', +} + +export enum BuilderNames { + WRAPPED_BUILDER = 'WrappedBuilder', + WRAP_BUILDER = 'wrapBuilder' } export enum ConditionNames { @@ -363,7 +377,9 @@ export const INTERMEDIATE_IMPORT_SOURCE: Map = new Map implements AnimatableArithmetic { + pointX: number = 1 + pointY: number = 1 + plus(rhs: AnimatableArithmetic): PointVector {return new PointVector()} + subtract(rhs: AnimatableArithmetic): PointVector{return new PointVector()} + multiply(scale: number): PointVector{return new PointVector()} + equals(rhs: AnimatableArithmetic): boolean{return true} +} + +@AnimatableExtend +function animatablePoint (this: TextAttribute, point: PointVector): this{ + this.width(point.pointX); + this.height(point.pointY); + return this +} + +@Entry() @Component -struct AnimatablePropertyExample { +struct MyStateSample { + @State width1: number = 80 build() { - Column() { + Column(){ Text("AnimatableProperty") - .backgroundColor(Color.Red) - .animation({ duration: 2000, curve: Curve.Ease }) - .fontSize(20) - .animation({ duration: 2000, curve: Curve.Ease }) - .width("100%") + .animatableWidth(this.width1) } } } \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/navigation/navdestination-basic.ets b/arkui-plugins/test/demo/mock/navigation/navdestination-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..c173e8ff33e3afe6731abecdc3cc6bafe6442f6e --- /dev/null +++ b/arkui-plugins/test/demo/mock/navigation/navdestination-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, NavDestination, Column, Button } from "@ohos.arkui.component" + +@Component +struct MyStateSample { + + build() { + NavDestination(){ + Column(){ + Button('abc') + } + }.width(80) + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/navigation/navigation-basic.ets b/arkui-plugins/test/demo/mock/navigation/navigation-basic.ets new file mode 100644 index 0000000000000000000000000000000000000000..932f7b870908e85a91181c9eca58946b43a3fa2d --- /dev/null +++ b/arkui-plugins/test/demo/mock/navigation/navigation-basic.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, Navigation, NavPathStack, Column, Button } from "@ohos.arkui.component" + +@Component +struct MyStateSample { + pathStack: NavPathStack = new NavPathStack() + + build() { + Navigation(this.pathStack){ + Column(){ + Button('abc') + } + }.width(80) + } +} \ No newline at end of file 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 index 178e8e2da040a889326852cb48c7ba7fb0a53b82..440bd43bfb4a90fcebf069d55b79f4a57196f5e3 100644 --- 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 @@ -36,11 +36,15 @@ const animatableExtendTransform: Plugins = { parsed: uiTransform().parsed, }; -const pluginTester = new PluginTester('test basic animatableExtend transform', buildConfig); +const pluginTester = new PluginTester('test basic AnimatableExtend transform', buildConfig); const expectedScript: string = ` import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; +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 { TextAttribute as TextAttribute } from "arkui.component.text"; @@ -58,9 +62,9 @@ import { EntryPoint as EntryPoint } from "arkui.UserView"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { Text as Text, Column as Column, Component as Component, Color as Color, Curve as Curve } from "@ohos.arkui.component"; +import { Component as Component, TextAttribute as TextAttribute, Text as Text, Entry as Entry, AnimatableArithmetic as AnimatableArithmetic, Column as Column, AnimatableExtend as AnimatableExtend } from "@ohos.arkui.component"; -import { Entry as Entry } from "@ohos.arkui.component"; +import { State as State } from "@ohos.arkui.stateManagement"; function main() {} @@ -70,12 +74,77 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ pagePath: "../../../animation/animatable-extend-basic", pageFullPath: "test/demo/mock/animation/animatable-extend-basic", integratedHsp: "false", - } as NavInterface)); +} as NavInterface)); +@AnimatableExtend() function animatableWidth(this: TextAttribute, width: number): TextAttribute { + this.__createOrSetAnimatableProperty("animatableWidth", width, ((width: (number | AnimatableArithmetic)) => { + width = (width as number); + this.width(width); + this.height(width); + })); + return this; +} + +@AnimatableExtend() function animatablePoint(this: TextAttribute, point: PointVector): TextAttribute { + this.__createOrSetAnimatableProperty("animatablePoint", point, ((point: (number | AnimatableArithmetic)) => { + point = (point as PointVector); + this.width(point.pointX); + this.height(point.pointY); + })); + return this; +} + + +class Point { + public x: number = 2; + + public y: number = 3; + + public constructor() {} + +} + +class PointVector extends Array implements AnimatableArithmetic { + public pointX: number = 1; + + public pointY: number = 1; + + public plus(rhs: AnimatableArithmetic): PointVector { + return new PointVector(); + } + + public subtract(rhs: AnimatableArithmetic): PointVector { + return new PointVector(); + } + + public multiply(scale: number): PointVector { + return new PointVector(); + } + + public equals(rhs: AnimatableArithmetic): boolean { + return true; + } + + public constructor() {} + +} -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct AnimatablePropertyExample extends CustomComponent implements PageLifeCycle { - public __initializeStruct(initializers: (__Options_AnimatablePropertyExample | undefined), @memo() content: ((()=> void) | undefined)): void {} +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_width1 = STATE_MGMT_FACTORY.makeState(this, "width1", ((({let gensym___149527867 = initializers; + (((gensym___149527867) == (null)) ? undefined : gensym___149527867.width1)})) ?? (80))); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_width1?: IStateDecoratedVariable; + + public get width1(): number { + return this.__backing_width1!.get(); + } - public __updateStruct(initializers: (__Options_AnimatablePropertyExample | undefined)): void {} + public set width1(value: number) { + this.__backing_width1!.set(value); + } @memo() public build() { ColumnImpl(@memo() ((instance: ColumnAttribute): void => { @@ -105,14 +174,20 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } -@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_AnimatablePropertyExample { +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set width1(width1: (number | undefined)) + + get width1(): (number | undefined) + set __backing_width1(__backing_width1: (IStateDecoratedVariable | undefined)) + + get __backing_width1(): (IStateDecoratedVariable | undefined) } class __EntryWrapper extends EntryPoint { @memo() public entry(): void { - AnimatablePropertyExample._instantiateImpl(undefined, (() => { - return new AnimatablePropertyExample(); + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); }), undefined, undefined, undefined); } @@ -131,10 +206,10 @@ function testAnimatableExtendTransformer(this: PluginTestContext): void { } pluginTester.run( - 'test basic animation transform', + 'test basic AnimatableExtend transform', [animatableExtendTransform, uiNoRecheck, recheck], { - checked: [testAnimatableExtendTransformer], + 'checked:ui-no-recheck': [testAnimatableExtendTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts index e7fa71969cbc045c1e8216a0ce6fefa88d8875e2..63327e3c1c902909bf9be9df41ea7bac19e04285 100644 --- a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts @@ -122,19 +122,20 @@ class __EntryWrapper extends EntryPoint { const expectedHeader = ` - animationStart(value: AnimateParam | undefined): this - animationStop(value: AnimateParam | undefined): this + animationStart(value: (AnimateParam | undefined)): this + animationStop(value: (AnimateParam | undefined)): this `; function testAnimationTransformer(this: PluginTestContext): void { expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.declContexts?.['arkui.component.common']?.scriptSnapshot ?? '')).toContain(parseDumpSrc(expectedHeader)); } pluginTester.run( 'test basic animation transform', [animationTransform, uiNoRecheck, recheck], { - checked: [testAnimationTransformer], + 'checked:ui-no-recheck': [testAnimationTransformer], }, { stopAfter: 'checked', diff --git a/arkui-plugins/test/ut/ui-plugins/navigation/navdestination_basic.test.ts b/arkui-plugins/test/ut/ui-plugins/navigation/navdestination_basic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..25b6257ac8e20ca33363d353b11e9943ba4cc194 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/navigation/navdestination_basic.test.ts @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const NAVIGATION_DIR_PATH: string = 'navigation'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, NAVIGATION_DIR_PATH, 'navdestination-basic.ets'), +]; + +const navigationTransform: Plugins = { + name: 'navdestination', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test basic navdestination transform', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { NavDestinationAttribute as NavDestinationAttribute } from "arkui.component.navDestination"; + +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, NavDestination as NavDestination, Column as Column, Button as Button } from "@ohos.arkui.component"; + +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() { + NavDestination(@memo() ((instance: NavDestinationAttribute): void => { + instance.width(80); + return; + }), { + moduleName: "entry", + pagePath: "../../../navigation/navdestination-basic", + }, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Button(undefined, "abc", undefined, undefined); + })); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + +} +`; + +const expectedHeader = + ` + @memo() export function NavDestination(style: (@memo() ((instance: NavDestinationAttribute)=> void) | undefined), moduleInfo: NavigationModuleInfo, @memo() content_?: (()=> void)): void + `; + +const expectedHeader1 = `export interface NavigationModuleInfo { + set moduleName(moduleName: string) + + get moduleName(): string + set pagePath(pagePath: string) + + get pagePath(): string +}`; + +function testNavDestinationTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.declContexts?.['arkui.component.navDestination']?.scriptSnapshot ?? '')).toContain(parseDumpSrc(expectedHeader)); + expect(parseDumpSrc(this.declContexts?.['arkui.component.navDestination']?.scriptSnapshot ?? '')).toContain(parseDumpSrc(expectedHeader1)); +} + +pluginTester.run( + 'test basic NavDestination transform', + [navigationTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testNavDestinationTransformer], + }, + { + stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.navDestination'] }, + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/navigation/navigation_basic.test.ts b/arkui-plugins/test/ut/ui-plugins/navigation/navigation_basic.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..087d303ad4d2944d2549555c3d6e2367e17e81f7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/navigation/navigation_basic.test.ts @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; +import { parseDumpSrc } from '../../../utils/parse-string'; +import { recheck, uiNoRecheck } from '../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { uiTransform } from '../../../../ui-plugins'; +import { Plugins } from '../../../../common/plugin-context'; + +const NAVIGATION_DIR_PATH: string = 'navigation'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, NAVIGATION_DIR_PATH, 'navigation-basic.ets'), +]; + +const navigationTransform: Plugins = { + name: 'navigation', + parsed: uiTransform().parsed, +}; + +const pluginTester = new PluginTester('test basic navigation transform', buildConfig); + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { NavigationAttribute as NavigationAttribute } from "arkui.component.navigation"; + +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, Navigation as Navigation, NavPathStack as NavPathStack, Column as Column, Button as Button } from "@ohos.arkui.component"; + +function main() {} + + + +@Component() final struct MyStateSample extends CustomComponent { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_pathStack = ((({let gensym___1107384 = initializers; + (((gensym___1107384) == (null)) ? undefined : gensym___1107384.pathStack)})) ?? (new NavPathStack())); + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_pathStack?: NavPathStack; + + public get pathStack(): NavPathStack { + return (this.__backing_pathStack as NavPathStack); + } + + public set pathStack(value: NavPathStack) { + this.__backing_pathStack = value; + } + + @memo() public build() { + Navigation(@memo() ((instance: NavigationAttribute): void => { + instance.width(80); + return; + }), this.pathStack, { + moduleName: "entry", + pagePath: "../../../navigation/navigation-basic", + isUserCreateStack: true, + }, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Button(undefined, "abc", undefined, undefined); + })); + })); + } + + private constructor() {} + +} + +@Component() export interface __Options_MyStateSample { + set pathStack(pathStack: (NavPathStack | undefined)) + + get pathStack(): (NavPathStack | undefined) + +} +`; + +const expectedHeader = + ` + @memo() export function Navigation(style: (@memo() ((instance: NavigationAttribute)=> void) | undefined), pathInfos: NavPathStack, moduleInfo: NavigationModuleInfo, @memo() content_?: (()=> void)): void + `; + +const expectedHeader1 = `export interface NavigationModuleInfo { + set moduleName(moduleName: string) + + get moduleName(): string + set pagePath(pagePath: string) + + get pagePath(): string + set isUserCreateStack(isUserCreateStack: boolean) + + get isUserCreateStack(): boolean +}`; + +function testNavigationTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.declContexts?.['arkui.component.navigation']?.scriptSnapshot ?? '')).toContain(parseDumpSrc(expectedHeader)); + expect(parseDumpSrc(this.declContexts?.['arkui.component.navigation']?.scriptSnapshot ?? '')).toContain(parseDumpSrc(expectedHeader1)); +} + +pluginTester.run( + 'test basic navigation transform', + [navigationTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testNavigationTransformer], + }, + { + stopAfter: 'checked', + tracing: { externalSourceNames: ['arkui.component.navigation'] }, + } +); 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 aba68967cd55f7aa67be5962fa72a91821f137ad..2b52188796e2f250bfe269961edb8309e281865e 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 @@ -18,6 +18,7 @@ import { ImportCollector } from '../../common/import-collector'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './utils'; import { factory } from './factory'; import { ProjectConfig } from '../../common/plugin-context'; +import { MetaDataCollector } from '../../common/metadata-collector'; export class BuilderLambdaTransformer extends AbstractVisitor { projectConfig: ProjectConfig | undefined; @@ -33,6 +34,7 @@ export class BuilderLambdaTransformer extends AbstractVisitor { } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + MetaDataCollector.getInstance().setProjectConfig(this.projectConfig).setAbsName(this.program?.absName); if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { const lambda = factory.transformBuilderLambda(beforeChildren); return this.visitEachChild(lambda); diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 7ff48d4bb7f4605166b1e5575956470c99dea0fc..5a0282010ccfa6b0f7a5c96cee71ebeea82ca910 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -14,6 +14,7 @@ */ import * as arkts from '@koalaui/libarkts'; +import * as path from 'path'; import { BuilderLambdaNames } from '../utils'; import { backingField, @@ -66,6 +67,9 @@ import { ConditionNames, DecoratorIntrinsicNames, DecoratorNames, + NavigationNames, + ARKUI_COMPONENT_NAVDESTINATION, + ARKUI_COMPONENT_NAVIGATION, } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; import { @@ -76,6 +80,8 @@ import { import { TypeRecord } from '../../collectors/utils/collect-types'; import { BuilderFactory } from './builder-factory'; import { StyleInternalsVisitor } from './style-internals-visitor'; +import { MetaDataCollector } from '../../common/metadata-collector'; +import { getRelativePagePath } from '../entry-translators/utils'; export class factory { /** @@ -85,11 +91,24 @@ export class factory { node: arkts.MethodDefinition, prefixArgs: arkts.ETSParameterExpression[], newAnno: arkts.AnnotationUsage[], - newName: string | undefined + newName: string | undefined, + externalSourceName?: string, ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; const isFunctionCall: boolean = node.name.name !== BuilderLambdaNames.ORIGIN_METHOD_NAME; - const newParams: arkts.Expression[] = [...prefixArgs, ...func.params]; + let newParams: arkts.Expression[] = []; + if (func.params.length > 0) { + newParams.push(...prefixArgs); + newParams.push(...func.params.slice(0, func.params.length - 1)); + if ( + (externalSourceName === ARKUI_COMPONENT_NAVIGATION && node.name.name === NavigationNames.NAVIGATION) || + (externalSourceName === ARKUI_COMPONENT_NAVDESTINATION && + node.name.name === NavigationNames.NAVDESTINATION) + ) { + newParams.push(this.createModuleInfoArgForNavigation()); + } + newParams.push(func.params.at(func.params.length - 1)!); + } const updateFunc = arkts.factory .updateScriptFunction( func, @@ -488,6 +507,15 @@ export class factory { leaf.arguments, params, (arg, param, index) => { + if ( + (type?.name === NavigationNames.NAVIGATION || type?.name === NavigationNames.NAVDESTINATION) && + index === params.length - 1 + ) { + const isUserCreateStack = + type?.name === NavigationNames.NAVIGATION ? leaf.arguments.length > 1 : undefined; + const moduleInfoObjExpr = factory.moduleInfoObjExpr(isUserCreateStack); + args.push(this.createOrUpdateArgInBuilderLambda(moduleInfoObjExpr, undefined, undefined)); + } let modifiedArg: arkts.AstNode | undefined; if (index === params.length - 2 && !arg) { modifiedArg = this.createSecondLastArgInBuilderLambda(secondLastArgInfo); @@ -558,7 +586,54 @@ export class factory { } /** - * update if-else in a builder lambda call's arguments. + * generate `moduleInfo: NavigationModuleInfo` in `@ComponentBuilder` Navigation and NavDestination + */ + static createModuleInfoArgForNavigation(): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + NavigationNames.MODULE_INFO, + UIFactory.createTypeReferenceFromString(NavigationNames.NAVIGATION_MODULE_INFO) + ), + undefined + ); + } + + /** + * create moduleInfo param in Navigation and NavDestination + */ + static moduleInfoObjExpr(isUserCreateStack?: boolean): arkts.ObjectExpression { + const projectConfig = MetaDataCollector.getInstance().projectConfig; + const fileAbsName = MetaDataCollector.getInstance().fileAbsName; + const projectRoot = projectConfig?.moduleRootPath + ? path.join(projectConfig.moduleRootPath, 'src', 'main', 'ets') + : ''; + const properties = [ + arkts.factory.createProperty( + arkts.factory.createIdentifier(NavigationNames.MODULE_NAME), + arkts.factory.createStringLiteral(projectConfig?.moduleName ?? '') + ), + arkts.factory.createProperty( + arkts.factory.createIdentifier(NavigationNames.PAGE_PATH), + arkts.factory.createStringLiteral(getRelativePagePath(projectRoot, fileAbsName ?? '')) + ), + ]; + if (isUserCreateStack !== undefined) { + properties.push( + arkts.factory.createProperty( + arkts.factory.createIdentifier(NavigationNames.IS_USER_CREATE_STATCK), + arkts.factory.createBooleanLiteral(isUserCreateStack) + ) + ); + } + return arkts.factory.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + properties, + false + ); + } + + /** + * update if-else in a builder lambda call's arguments. */ static updateIfElseContentBodyInBuilderLambda( statement: arkts.AstNode, diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index aaa17782067466fea49acc04fd25a8079a1667cd..2cb53af0402b1a5f4fd2e1554671756d92dc30a9 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -37,6 +37,8 @@ import { ResourceInfo, ScopeInfoCollection, isForEachDecl, + initNavigation, + RouterInfo } from './struct-translators/utils'; import { collectCustomComponentScopeInfo, @@ -46,6 +48,7 @@ import { isSpecificNewClass, } from './utils'; import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; +import { MetaDataCollector } from '../common/metadata-collector'; import { InteroperAbilityNames } from './interop/predefines'; import { generateBuilderCompatible } from './interop/builder-interop'; import { builderRewriteByType } from './builder-lambda-translators/builder-factory'; @@ -55,8 +58,9 @@ export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; private legacyBuilderSet: Set = new Set(); projectConfig: ProjectConfig | undefined; - aceBuildJson: LoaderJson; + aceBuildJson: Partial; resourceInfo: ResourceInfo; + navigationInfo: Map; constructor(projectConfig: ProjectConfig | undefined) { super(); @@ -66,6 +70,7 @@ export class CheckedTransformer extends AbstractVisitor { this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); this.legacyBuilderSet = new Set(); this.initBuilderMap(); + this.navigationInfo = initNavigation(this.aceBuildJson); } initBuilderMap(): void { @@ -94,6 +99,7 @@ export class CheckedTransformer extends AbstractVisitor { ImportCollector.getInstance().reset(); DeclarationCollector.getInstance().reset(); LogCollector.getInstance().reset(); + MetaDataCollector.getInstance().reset(); } enter(node: arkts.AstNode): void { @@ -155,6 +161,7 @@ export class CheckedTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); + MetaDataCollector.getInstance().setProjectConfig(this.projectConfig).setAbsName(this.program?.absName) if (arkts.isCallExpression(beforeChildren)) { const decl = arkts.getDecl(beforeChildren.expression); if (arkts.isIdentifier(beforeChildren.expression) && this.isFromBuilder1_1(decl)) { @@ -195,7 +202,7 @@ export class CheckedTransformer extends AbstractVisitor { entryFactory.addMemoToEntryWrapperClassMethods(node); return node; } else if (arkts.isClassDeclaration(node)) { - return structFactory.transformNormalClass(node, this.externalSourceName); + return structFactory.transformNormalClass(node, this.isExternal, this.navigationInfo, this.program?.absName); } else if (arkts.isCallExpression(node)) { return structFactory.transformCallExpression(node, this.projectConfig, this.resourceInfo); } else if (arkts.isMethodDefinition(node) && isForEachDecl(node, this.externalSourceName)) { diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index 37db0024f5b3ea6ce69facc144e4fa580015e668..7937efdeeb95a9513785bffa9d50c1867521dfed 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -53,6 +53,8 @@ import { ENTRY_POINT_IMPORT_SOURCE_NAME, NavigationNames, EntryWrapperNames, + ARKUI_COMPONENT_NAVIGATION, + ARKUI_COMPONENT_NAVDESTINATION } from '../common/predefines'; import { generateInstantiateInterop } from './interop/interop'; @@ -208,6 +210,15 @@ export class ComponentTransformer extends AbstractVisitor { navInterface.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; return arkts.factory.updateEtsScript(node, [...node.statements, navInterface]); } + if (this.isExternal && (this.externalSourceName === ARKUI_COMPONENT_NAVIGATION || + this.externalSourceName === ARKUI_COMPONENT_NAVDESTINATION) + ) { + const navModuleInfo = entryFactory.createNavModuleInfo( + this.externalSourceName === ARKUI_COMPONENT_NAVIGATION + ); + navModuleInfo.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + return arkts.factory.updateEtsScript(node, [...node.statements, navModuleInfo]); + } if (this.isExternal && this.componentInterfaceCollection.length === 0 && this.entryNames.length === 0) { return node; } @@ -277,7 +288,7 @@ export class ComponentTransformer extends AbstractVisitor { node.definition.implements, undefined, node.definition.super, - [entryFactory.generateRegisterNamedRouter(), ...node.definition.body], + [entryFactory.generateRegisterNamedRouter(), entryFactory.generateNavigationBuilderRegister(), ...node.definition.body], node.definition.modifiers, arkts.classDefinitionFlags(node.definition) ) diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index e4dd29d224a73bb929913285ac9d5fb2cb602a91..0efe3c8ab69f0400f4a636e3c005bb34597e0ff0 100644 --- a/arkui-plugins/ui-plugins/entry-translators/factory.ts +++ b/arkui-plugins/ui-plugins/entry-translators/factory.ts @@ -16,7 +16,12 @@ import * as arkts from '@koalaui/libarkts'; import * as path from 'path'; import { annotation, createAndInsertImportDeclaration } from '../../common/arkts-utils'; -import { ENTRY_POINT_IMPORT_SOURCE_NAME, EntryWrapperNames, NavigationNames } from '../../common/predefines'; +import { + ENTRY_POINT_IMPORT_SOURCE_NAME, + EntryWrapperNames, + NavigationNames, + BuilderNames, +} from '../../common/predefines'; import { ProjectConfig } from '../../common/plugin-context'; import { factory as uiFactory } from '../ui-factory'; import { getRelativePagePath } from './utils'; @@ -464,4 +469,76 @@ export class factory { modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, }); } + + /** + * generate interface NavigationModuleInfo in `arkui.component.navDestination` & `arkui.component.navDestination` + */ + static createNavModuleInfo(isNavigation: boolean): arkts.TSInterfaceDeclaration { + const body = [ + factory.createClassProp(NavigationNames.MODULE_NAME), + factory.createClassProp(NavigationNames.PAGE_PATH), + ]; + if (isNavigation) { + body.push( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(NavigationNames.IS_USER_CREATE_STATCK), + undefined, + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ) + ); + } + const NavigationModuleInfo = arkts.factory.createInterfaceDeclaration( + [], + arkts.factory.createIdentifier(NavigationNames.NAVIGATION_MODULE_INFO), + undefined, + arkts.factory.createInterfaceBody(body), + false, + false + ); + NavigationModuleInfo.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_EXPORT; + return NavigationModuleInfo; + } + + /** + * generate generateNavigationBuilderRegister method in header arkui.UserView + */ + static generateNavigationBuilderRegister() { + const params = [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier('name', uiFactory.createTypeReferenceFromString('string')), + undefined + ), + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'builder', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(BuilderNames.WRAPPED_BUILDER), + arkts.factory.createTSTypeParameterInstantiation([ + uiFactory.createTypeReferenceFromString('T'), + ]) + ) + ) + ), + undefined + ), + ]; + return uiFactory.createMethodDefinition({ + key: arkts.factory.createIdentifier(NavigationNames.NAVIGATION_BUILDER_REGISTER), + kind: arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + function: { + body: arkts.factory.createBlock([]), + typeParams: arkts.factory.createTypeParameterDeclaration( + [arkts.factory.createTypeParameter(arkts.factory.createIdentifier('T'))], + 0 + ), + 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/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index faea2deb790d25cdff160ac1ee17d2ede032e47a..9920dd453b15d3b040fe98505d2e858f7dd50a6f 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -57,6 +57,7 @@ import { findBuilderIndexInControllerOptions, ObservedAnnoInfo, getNoTransformationMembersInClass, + RouterInfo, } from './utils'; import { collectStateManagementTypeImport, generateThisBacking, hasDecorator } from '../property-translators/utils'; import { ComponentAttributeCache, isComponentAttributeInterface } from '../builder-lambda-translators/utils'; @@ -72,6 +73,9 @@ import { RESOURCE_TYPE, ARKUI_BUILDER_SOURCE_NAME, TypeNames, + NavigationNames, + ARKUI_COMPONENT_BUILDER, + BuilderNames, } from '../../common/predefines'; import { ObservedTranslator } from '../property-translators/index'; import { @@ -807,21 +811,26 @@ export class factory { return node; } - static transformETSGlobalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + static transformETSGlobalClass( + node: arkts.ClassDeclaration, + isExternal: boolean, + navigationInfo: Map, + fileName?: string, + externalSourceName?: string + ): arkts.ClassDeclaration { if (!node.definition) { return node; } const updatedBody = node.definition.body.map((member: arkts.AstNode) => { - arkts.isMethodDefinition(member) && PropertyFactory.addMemoToBuilderClassMethod(member); - if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { - member = arkts.factory.updateMethodDefinition( - member, - member.kind, - member.name, - factory.transformAnimatableExtend(member.scriptFunction), - member.modifiers, - false - ); + if (!arkts.isMethodDefinition(member)) { + return member; + } + PropertyFactory.addMemoToBuilderClassMethod(member); + if (hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { + return factory.transformAnimatableExtend(member); + } + if (member.name.name === '_$init$_' && !isExternal) { + return factory.transformRouterMapBuilder(member, navigationInfo, fileName); } return member; }); @@ -851,17 +860,88 @@ export class factory { ); } - static transformNormalClass(node: arkts.ClassDeclaration, externalSourceName?: string): arkts.ClassDeclaration { + static transformNormalClass( + node: arkts.ClassDeclaration, + isExternal: boolean, + navigationInfo: Map, + fileName?: string, + externalSourceName?: string + ): arkts.ClassDeclaration { if (!node.definition) { return node; } if (isEtsGlobalClass(node)) { - return this.transformETSGlobalClass(node, externalSourceName); + return this.transformETSGlobalClass(node, isExternal, navigationInfo, fileName, externalSourceName); } const newClassDef = factory.updateObservedTrackClassDef(node.definition); return arkts.factory.updateClassDeclaration(node, newClassDef); } + /* + * transform build functions in router map + */ + static transformRouterMapBuilder( + node: arkts.MethodDefinition, + navigationInfo: Map, + fileName?: string + ) { + if (node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { + return UIFactory.updateMethodDefinition(node, { + function: { + body: arkts.factory.updateBlock(node.scriptFunction.body, [ + ...node.scriptFunction.body.statements, + ...factory.createNavigationInit(navigationInfo, fileName), + ]), + }, + }); + } + return node; + } + + /* + * for current file's build functions in router map, create NavigationBuilderRegister functions + */ + static createNavigationInit( + navigationInfo: Map, + fileName?: string + ): arkts.ExpressionStatement[] { + const builderStatements: arkts.ExpressionStatement[] = []; + if (!fileName) return builderStatements; + const routerInfoArr: Array | undefined = navigationInfo.get(fileName); + routerInfoArr?.forEach((item) => { + builderStatements.push(factory.createNavigationRegister(item)); + }); + return builderStatements; + } + + /* + * create NavigationBuilderRegister('...', wrapBuilder(...)) + */ + static createNavigationRegister(routerInfo: RouterInfo): arkts.ExpressionStatement { + ImportCollector.getInstance().collectSource(BuilderNames.WRAP_BUILDER, ARKUI_COMPONENT_BUILDER); + ImportCollector.getInstance().collectImport(BuilderNames.WRAP_BUILDER); + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('EntryPoint'), + arkts.factory.createIdentifier(NavigationNames.NAVIGATION_BUILDER_REGISTER), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createStringLiteral(routerInfo.name), + arkts.factory.createCallExpression( + arkts.factory.createIdentifier(BuilderNames.WRAP_BUILDER), + undefined, + [arkts.factory.createIdentifier(routerInfo.buildFunction)] + ), + ] + ) + ); + } + /** * transform class definition with , `@ObservedV2` and `@Trace`, `@Observed` or `@Trace`. */ @@ -1013,13 +1093,17 @@ export class factory { /* * transform @AnimatableExtend method */ - static transformAnimatableExtend(node: arkts.ScriptFunction): arkts.ScriptFunction { - if (!arkts.isEtsParameterExpression(node.params[1]) || !node.body || !arkts.isBlockStatement(node.body)) { + static transformAnimatableExtend(node: arkts.MethodDefinition): arkts.MethodDefinition { + if ( + !arkts.isEtsParameterExpression(node.scriptFunction.params[1]) || + !node.scriptFunction.body || + !arkts.isBlockStatement(node.scriptFunction.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 funcName: arkts.StringLiteral = arkts.factory.createStringLiteral(node.scriptFunction.id?.name!); + const paramValue: arkts.ETSParameterExpression = node.scriptFunction.params[1]; + const originStatements: arkts.Statement[] = [...node.scriptFunction.body.statements]; const createOrSetStatement = arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( arkts.factory.createMemberExpression( @@ -1037,18 +1121,11 @@ export class factory { ] ) ); - 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 - ); + return UIFactory.updateMethodDefinition(node, { + function: { + body: arkts.factory.createBlock([createOrSetStatement, originStatements[originStatements.length - 1]]), + }, + }); } /* diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index 722453b7a966f09e1018ec4a2d89d0d09ede4e16..2552e947cd06a3ae502978e0cf93c3b478b60d28 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -25,6 +25,8 @@ import { ResourceInfo, loadBuildJson, initResourceInfo, + initNavigation, + RouterInfo } from './utils'; import { factory } from './factory'; import { isEntryWrapperClass } from '../entry-translators/utils'; @@ -37,8 +39,9 @@ import { PropertyCache } from '../property-translators/cache/propertyCache'; export class StructTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; - aceBuildJson: LoaderJson; + aceBuildJson: Partial; resourceInfo: ResourceInfo; + navigationInfo: Map; constructor(projectConfig: ProjectConfig | undefined) { super(); @@ -46,6 +49,7 @@ export class StructTransformer extends AbstractVisitor { this.scope = { customComponents: [] }; this.aceBuildJson = loadBuildJson(this.projectConfig); this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); + this.navigationInfo = initNavigation(this.aceBuildJson); } reset(): void { @@ -97,7 +101,7 @@ export class StructTransformer extends AbstractVisitor { entryFactory.addMemoToEntryWrapperClassMethods(node); return node; } else if (arkts.isClassDeclaration(node)) { - return factory.transformNormalClass(node); + return factory.transformNormalClass(node, this.isExternal, this.navigationInfo, this.program?.absName); } else if (arkts.isCallExpression(node) && isResourceNode(node)) { return factory.transformResource(node, this.projectConfig, this.resourceInfo); } else if (isArkUICompatible(node)) { diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index ecba338d0f2218eb219efc9b0dbd8431ded149ac..0cc6ea2ee6a08bca5b871e0f2b10aba02f7030ab 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -55,8 +55,21 @@ export interface ResourceInfo { rawfile: Set; } +export interface RouterMap { + ohmurl: string; + name: string; + pageSourceFile: string; + buildFunction: string; +} + +export interface RouterInfo { + name: string, + buildFunction: string, +} + export interface LoaderJson { hspResourcesMap: Record; + routerMap: RouterMap[]; } export interface ResourceParameter { @@ -137,7 +150,7 @@ export function isForEachCall(node: arkts.CallExpression): boolean { * * @param projectConfig configuration information of the project. */ -export function loadBuildJson(projectConfig: ProjectConfig | undefined): any { +export function loadBuildJson(projectConfig: ProjectConfig | undefined): Partial { if (!!projectConfig && projectConfig.buildLoaderJson && fs.existsSync(projectConfig.buildLoaderJson)) { try { const content = fs.readFileSync(projectConfig.buildLoaderJson, 'utf-8'); @@ -156,7 +169,7 @@ export function loadBuildJson(projectConfig: ProjectConfig | undefined): any { * @param projectConfig configuration information of the project. * @param aceBuildJson content of the file 'loader.json'. */ -export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBuildJson: LoaderJson): ResourceInfo { +export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBuildJson: Partial): ResourceInfo { let resourcesList: ResourceList = { app: new Map>(), sys: new Map>(), @@ -179,7 +192,7 @@ export function initResourceInfo(projectConfig: ProjectConfig | undefined, aceBu function readAppResource( resourcesList: ResourceList, projectConfig: ProjectConfig, - aceBuildJson: LoaderJson, + aceBuildJson: Partial, rawfile: Set ): void { if ('hspResourcesMap' in aceBuildJson && aceBuildJson.hspResourcesMap) { @@ -267,15 +280,16 @@ function rescordResourceNameAndIdMap(resourceMap: Map, projectConfig: ProjectConfig, resourcesList: ResourceList): void { projectConfig.hspResourcesMap = true; - for (const hspName in aceBuildJson.hspResourcesMap) { - if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) { + const hspResourcesMap = aceBuildJson?.hspResourcesMap ?? {}; + for (const hspName in hspResourcesMap) { + if (fs.existsSync(hspResourcesMap[hspName])) { const resourceMap: ResourceMap = new Map>(); resourcesList[hspName] = new Map>(); - const hspResource: string = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8'); + const hspResource: string = fs.readFileSync(hspResourcesMap[hspName], 'utf-8'); const resourceArr: string[] = hspResource.split(/\n/); - processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]); + processResourceArr(resourceArr, resourceMap, hspResourcesMap[hspName]); for (const [key, value] of resourceMap) { resourcesList[hspName].set(key, value); } @@ -604,3 +618,25 @@ export function getNoTransformationMembersInClass( ) ); } + +/** + * Initializes routerInfo which maps absolute file paths to corresponding build functions + * + * @param aceBuildJson content of the file 'loader.json'. + */ +export function initNavigation(aceBuildJson: Partial): Map { + const routerInfo: Map = new Map(); + if (aceBuildJson && aceBuildJson.routerMap && Array.isArray(aceBuildJson.routerMap)) { + aceBuildJson.routerMap.forEach((item) => { + if (item.pageSourceFile && item.name && item.buildFunction) { + const filePath = path.resolve(item.pageSourceFile); + if (routerInfo.has(filePath)) { + routerInfo.get(filePath)!.push({ name: item.name, buildFunction: item.buildFunction }); + } else { + routerInfo.set(filePath, [{ name: item.name, buildFunction: item.buildFunction }]); + } + } + }); + } + return routerInfo; +}