diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 642d0219a8693c3b6e6ab4fad069871cdb3595ab..68abfc5bb60b8d99b9b43ccf2be35f7fa7c9890c 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -128,6 +128,7 @@ export enum InnerComponentAttributes { export enum DecoratorNames { STATE = 'State', STORAGE_LINK = 'StorageLink', + STORAGE_PROP = 'StorageProp', LINK = 'Link', PROP = 'Prop', PROVIDE = 'Provide', @@ -139,6 +140,7 @@ export enum DecoratorNames { BUILDER_PARAM = 'BuilderParam', BUILDER = 'Builder', CUSTOM_DIALOG = 'CustomDialog', + LOCAL_STORAGE_PROP = 'LocalStorageProp', LOCAL_STORAGE_LINK = 'LocalStorageLink', REUSABLE = 'Reusable', TRACK = 'Track', @@ -181,6 +183,7 @@ export enum StateManagementTypes { STORAGE_PROP_REF_DECORATED = 'IStoragePropRefDecoratedVariable', LOCAL_STORAGE_LINK_DECORATED = 'ILocalStorageLinkDecoratedVariable', PROP_DECORATED = 'IPropDecoratedVariable', + SYNCED_PROPERTY = 'SyncedProperty', PROVIDE_DECORATED = 'IProvideDecoratedVariable', CONSUME_DECORATED = 'IConsumeDecoratedVariable', OBJECT_LINK_DECORATED = 'IObjectLinkDecoratedVariable', @@ -206,6 +209,7 @@ export enum StateManagementTypes { UPDATE = 'update', MAKE_STATE = 'makeState', MAKE_LINK = 'makeLink', + MAKE_PROP = 'makeProp', MAKE_PROP_REF = 'makePropRef', MAKE_LOCAL = 'makeLocal', MAKE_STATIC_LOCAL = 'makeStaticLocal', @@ -273,10 +277,13 @@ export const RESOURCE_TYPE: Record = { export const DECORATOR_TYPE_MAP = new Map([ [DecoratorNames.STATE, StateManagementTypes.STATE_DECORATED], [DecoratorNames.LINK, StateManagementTypes.LINK_SOURCE_TYPE], + [DecoratorNames.PROP, StateManagementTypes.PROP_DECORATED], [DecoratorNames.PROP_REF, StateManagementTypes.PROP_REF_DECORATED], [DecoratorNames.STORAGE_LINK, StateManagementTypes.STORAGE_LINK_DECORATED], + [DecoratorNames.STORAGE_PROP, StateManagementTypes.STORAGE_PROP_REF_DECORATED], [DecoratorNames.STORAGE_PROP_REF, StateManagementTypes.STORAGE_PROP_REF_DECORATED], [DecoratorNames.LOCAL_STORAGE_PROP_REF, StateManagementTypes.LOCAL_STORAGE_PROP_REF_DECORATED], + [DecoratorNames.LOCAL_STORAGE_PROP, StateManagementTypes.SYNCED_PROPERTY], [DecoratorNames.LOCAL_STORAGE_LINK, StateManagementTypes.LOCAL_STORAGE_LINK_DECORATED], [DecoratorNames.OBJECT_LINK, StateManagementTypes.OBJECT_LINK_DECORATED], [DecoratorNames.PROVIDE, StateManagementTypes.PROVIDE_DECORATED], @@ -294,9 +301,11 @@ export const INTERMEDIATE_IMPORT_SOURCE: Map = new Map = new Map = new Map = new Map([ [Dollars.TRANSFORM_DOLLAR_RESOURCE, 'arkui.component.resources'], [Dollars.TRANSFORM_DOLLAR_RAWFILE, 'arkui.component.resources'], + [StateManagementTypes.SYNCED_PROPERTY, 'arkui.stateManagement.runtime'], [StateManagementTypes.STORAGE_LINK_STATE, 'arkui.stateManagement.runtime'], [StateManagementTypes.OBSERVABLE_PROXY, 'arkui.stateManagement.runtime'], [StateManagementTypes.PROP_STATE, 'arkui.stateManagement.runtime'], diff --git a/arkui-plugins/test/demo/mock/component/declare-component.ets b/arkui-plugins/test/demo/mock/component/declare-component.ets index 2241b11639c6016377a913c959a97fb659f45a43..aa0142368b5354a8d4346902c8ce4f6f43cbdd6c 100644 --- a/arkui-plugins/test/demo/mock/component/declare-component.ets +++ b/arkui-plugins/test/demo/mock/component/declare-component.ets @@ -14,12 +14,12 @@ */ import { Component, ResourceStr, Builder} from "@ohos.arkui.component" -import { PropRef, State } from "@ohos.arkui.stateManagement" +import { Prop, State } from "@ohos.arkui.stateManagement" @Component export declare struct SwipeRefresher { - @PropRef content?: ResourceStr; - @PropRef isLoading: boolean; + @Prop content?: ResourceStr; + @Prop isLoading: boolean; @State code: number; @Builder build(): void; diff --git a/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets index 57f1d3fee9554eb7d3980cc9c4f1eb7c0b76403a..65065c3e573871304305edf1856f30a5c953a00e 100644 --- a/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets +++ b/arkui-plugins/test/demo/mock/decorators/decorator-no-type.ets @@ -14,7 +14,7 @@ */ import { Component, ComponentV2, CustomDialog } from "@ohos.arkui.component" -import { State, Provide, Event, Local, Param } from "@ohos.arkui.stateManagement" +import { State, Prop, Provide, Event, Local, Param } from "@ohos.arkui.stateManagement" import { Provider, Consumer, Once, Observed, ObservedV2, Trace, Track } from "@ohos.arkui.stateManagement" class Per { @@ -44,6 +44,7 @@ class RR { @Component struct Parent { @State stateVar1 = new Per(6); + @Prop stateVar2 = new Array(3, 6, 8); @Provide stateVar3 = StateType.TYPE3; stateVar8 = (sr: string) => { }; diff --git a/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets b/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets index 70275e71b7ee837e4e38dcf674b415e3ca97b264..b04d84e93c8f0f6e83a0be4e0afb54c5704d60aa 100644 --- a/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets +++ b/arkui-plugins/test/demo/mock/decorators/link/link-to-link-prop-state.ets @@ -14,7 +14,7 @@ */ import { Component, Column, TextInput } from "@ohos.arkui.component" -import { Link, State, PropRef } from "@ohos.arkui.stateManagement" +import { Link, State, Prop } from "@ohos.arkui.stateManagement" @Component struct Parant { @@ -32,8 +32,8 @@ struct Parant { struct Child { @Link childText: string; @State childText2: string = 'sss'; - @PropRef childText3: string; - @PropRef childText4: string = 'cc'; + @Prop childText3: string; + @Prop childText4: string = 'cc'; build() { TextInput({ text: this.childText }) diff --git a/arkui-plugins/test/demo/mock/decorators/prop/prop-basic-type.ets b/arkui-plugins/test/demo/mock/decorators/prop/prop-basic-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..bcfff2d589ac24d532da6c04ddc2ca008acf5cf0 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/prop-basic-type.ets @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Prop } from "@ohos.arkui.stateManagement" + +@Component +struct PropParent { + @Prop propVar1: string = 'propVar1'; + @Prop propVar2: number = 50; + @Prop propVar3: boolean = true; + @Prop propVar4: undefined = undefined; + @Prop propVar5: null = null; + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop/prop-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/prop/prop-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..a2fbdc9d2387657602f4edd681f64b82a737f645 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/prop-complex-type.ets @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component } from "@ohos.arkui.component" +import { Prop } from "@ohos.arkui.stateManagement" + +class Per { + num: number; + constructor(num: number) { + this.num = num; + } +} + +enum PropType { + TYPE1 = 0, + TYPE2 = 1, + TYPE3 = 3 +} + +@Component +struct Parent { + @Prop propVar1: Per = new Per(6); + @Prop propVar2: Array = new Array(3,6,8); + @Prop propVar3: PropType = PropType.TYPE3; + @Prop propVar4: Set = new Set(new Array('aa', 'bb')); + @Prop propVar5: boolean[] = [true, false]; + @Prop propVar6: Array = new Array(new Per(7), new Per(11)); + @Prop propVar7: Per[] = [new Per(7), new Per(11)]; + @Prop propVar8: (sr: string)=>void = (sr: string)=>{}; + @Prop propVar9: Date = new Date('2025-4-23'); + @Prop propVar10: Map = new Map([[0, new Per(7)], [1, new Per(10)]]); + @Prop propVar11: string | number = 0.0; + @Prop propVar12: Set | Per = new Per(6); + + build() { + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.ets b/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.ets new file mode 100644 index 0000000000000000000000000000000000000000..38af709df0adab049df6551c806c570acaa01019 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/prop/state-to-prop.ets @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Text, Button, Column, ClickEvent } from "@ohos.arkui.component" +import { Prop, State } from "@ohos.arkui.stateManagement" + +@Component +struct CountDownComponent { + @Prop count: number = 0; + costOfOneAttempt: number = 1; + + build() { + Column() { + if (this.count > 0) { + Text('You have'+ this.count + 'Nuggets left') + } else { + Text('Game over!') + } + Button('Try again').onClick((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + }) + } + } +} + +@Component +struct ParentComponent { + @State countDownStartValue: number = 10; + + build() { + Column() { + Text('Grant' + this.countDownStartValue + 'nuggets to play.') + Button('+1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue += 1; + }) + Button('-1 - Nuggets in New Game').onClick((e: ClickEvent) => { + this.countDownStartValue -= 1; + }) + CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 }) + } + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets index 20aaeef4d6ad5cca44d2ad9639332abb3cf521b5..86703f8b58e981eff528f1fd518c952b5a66a735 100644 --- a/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets +++ b/arkui-plugins/test/demo/mock/decorators/require/basic-require.ets @@ -14,7 +14,7 @@ */ import { Component, ComponentV2, BuilderParam } from "@ohos.arkui.component" -import { State, Require, Provide, Param } from "@ohos.arkui.stateManagement" +import { State, Require, Prop, Provide, Param } from "@ohos.arkui.stateManagement" @Component struct MyStateSample { @@ -24,6 +24,7 @@ struct MyStateSample { @Require @State select0: number; @Require @State select3: number | null; @Require @State select4: undefined; + @Require @Prop select1: string; @Require @Provide({ alias: '15' }) select2: string[]; @Require @Provide({ alias: 't' }) select6: string[] | undefined | string; @Require @BuilderParam builder: () => void; diff --git a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets index 0a1e970c2ebd908f715563f0f8b7fe7c7e5dc631..547ed62ea0ace3b2aac355c254ae89cf206fd4cf 100644 --- a/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/reusable/reusable-basic.ets @@ -14,18 +14,19 @@ */ import { Component, Reusable} from "@ohos.arkui.component" -import { State } from "@ohos.arkui.stateManagement" +import { State, Prop } from "@ohos.arkui.stateManagement" @Component struct MyStateSample { build() { - Child({ num1: 5 } ) + Child({ num: 5 } ) } } @Component @Reusable struct Child { + @Prop num: number = 1 @State num1: number = 2 build() { diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets new file mode 100644 index 0000000000000000000000000000000000000000..dd25eb7fb008b724d932df2a6d2ed8e03c4ea069 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-appstorage.ets @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry, Column, Text, ClickEvent } from "@ohos.arkui.component" +import { StorageProp, AppStorage } from "@ohos.arkui.stateManagement" + +class Data { + code: number; + + constructor(code: number) { + this.code = code; + } +} + +AppStorage.setOrCreate('PropA', 47); +AppStorage.setOrCreate('PropB', new Data(50)); + +@Entry +@Component +struct Index { + @StorageProp('PropA') storageProp: number = 1; + @StorageProp('PropB') storagePropObject: Data = new Data(1); + + build() { + Column() { + Text(`From AppStorage ${this.storageProp}`) + .onClick((e: ClickEvent) => { + this.storageProp += 1; + }) + + Text(`From AppStorage ${this.storagePropObject.code}`) + .onClick((e: ClickEvent) => { + this.storagePropObject.code += 1; + }) + } + } +} diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..50c2ea128a534ea463a12c780b1fa27ab051fe10 --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-complex-type.ets @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StorageProp } from "@ohos.arkui.stateManagement" + +class Person{ + name: string = '' + constructor(name: string){} +} + +enum Status { + Success = 200, + NotFound = 404, + ServerError = 500 +} + +@Entry +@Component +struct MyStateSample { + @StorageProp('Prop1') arrayB: number[] = [1,2,3]; + @StorageProp('Prop2') objectB: Object = {}; + @StorageProp('Prop3') dateB: Date = new Date('2021-09-09'); + @StorageProp('Prop4') setB: Set = new Set(); + @StorageProp('Prop5') mapB: Map = new Map(); + @StorageProp('Prop7') classB: Person = new Person("Kevin"); + @StorageProp('Prop8') enumB: Status = Status.NotFound; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..95d4ed58d42aaa7f76fbdfe8b1c6798ac2e22b6f --- /dev/null +++ b/arkui-plugins/test/demo/mock/decorators/storageprop/storageprop-primitive-type.ets @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Entry } from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { StorageProp } from "@ohos.arkui.stateManagement" + +@Entry +@Component +struct MyStateSample { + @StorageProp('Prop1') numB: number = 43; + @StorageProp('Prop2') stringB: string = 'BB'; + @StorageProp('Prop3') booleanB: boolean = false; + + build() {} +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets index 8fb667a2f32689110cb08d7f8a85cf665ae16c22..4c36212e7da874ab1268d3e671f7f6d69c867ee3 100644 --- a/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets +++ b/arkui-plugins/test/demo/mock/decorators/watch/watch-basic.ets @@ -14,7 +14,7 @@ */ import { Component, Entry, Column } from "@ohos.arkui.component" -import { State, PropRef, StorageLink, StoragePropRef, Link, Watch, ObjectLink, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" +import { State, Prop, StorageLink, StorageProp, Link, Watch, ObjectLink, Observed, Track, Provide, Consume } from "@ohos.arkui.stateManagement" @Observed class A { @@ -26,10 +26,10 @@ class A { @Component struct MyStateSample { @State @Watch('stateOnChange') statevar: string = 'Hello World'; - @PropRef @Watch('propOnChange') propvar: string = 'Hello World'; + @Prop @Watch('propOnChange') propvar: string = 'Hello World'; @Link @Watch('linkOnChange') linkvar: string; @StorageLink('prop1') @Watch('storageLinkOnChange') storagelinkvar: string = 'Hello World'; - @StoragePropRef('prop2') @Watch('storagePropOnChange') storagepropvar: string = 'Hello World'; + @StorageProp('prop2') @Watch('storagePropOnChange') storagepropvar: string = 'Hello World'; @ObjectLink @Watch('objectLinkOnChange') objectlinkvar: A; @Provide @Watch('ProvideOnChange') providevar: string = 'Hello World'; diff --git a/arkui-plugins/test/demo/mock/imports/kit-import.ets b/arkui-plugins/test/demo/mock/imports/kit-import.ets index 37f13222ddab5a5bbfa5dddc9e4755cce678332e..5cd7771b28e08fdfdd73c40373362704b6f01bd6 100644 --- a/arkui-plugins/test/demo/mock/imports/kit-import.ets +++ b/arkui-plugins/test/demo/mock/imports/kit-import.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { PropRef, Column, Entry } from "@kit.ArkUI"; +import { Prop, Column, Entry } from "@kit.ArkUI"; import { Text, Component, ClickEvent } from "@ohos.arkui.component"; import { State } from "@ohos.arkui.stateManagement"; import { Button } from "arkui.component.button"; @@ -23,7 +23,7 @@ import hilog from "@ohos.hilog"; @Component struct A { @State a: string = "str"; - @PropRef b: string; + @Prop b: string; build() { Column() { diff --git a/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts index 7feafa2509b25be3198928ad9f6b8e9d55123cf8..6261ad5d528fcbf90be7e222a349ac309d78c360 100644 --- a/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/component/declare-component.test.ts @@ -42,12 +42,12 @@ import { CustomComponent as CustomComponent } from "arkui.component.customCompon import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; -import { PropRef as PropRef, State as State } from "@ohos.arkui.stateManagement"; +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; @Component() export declare final struct SwipeRefresher extends CustomComponent { - @PropRef() public content?: (ResourceStr | undefined); + @Prop() public content?: (ResourceStr | undefined); - @PropRef() public isLoading: boolean; + @Prop() public isLoading: boolean; @State() public code: number; @@ -61,9 +61,9 @@ import { PropRef as PropRef, State as State } from "@ohos.arkui.stateManagement" @Component() export declare interface __Options_SwipeRefresher { content?: (ResourceStr | undefined); - @PropRef() __backing_content?: (ResourceStr | undefined); + @Prop() __backing_content?: (ResourceStr | undefined); isLoading?: boolean; - @PropRef() __backing_isLoading?: boolean; + @Prop() __backing_isLoading?: boolean; code?: number; @State() __backing_code?: number; @@ -77,7 +77,7 @@ function testParsedTransformer(this: PluginTestContext): void { const expectedCheckedScript: string = ` import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; @@ -85,14 +85,14 @@ import { CustomComponent as CustomComponent } from "arkui.component.customCompon import { Component as Component, ResourceStr as ResourceStr, Builder as Builder } from "@ohos.arkui.component"; -import { PropRef as PropRef, State as State } from "@ohos.arkui.stateManagement"; +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; function main() {} @Component() export declare final struct SwipeRefresher extends CustomComponent { - @PropRef() public content?: (ResourceStr | undefined); + @Prop() public content?: (ResourceStr | undefined); - @PropRef() public isLoading: boolean; + @Prop() public isLoading: boolean; @State() public code: number; @@ -108,15 +108,15 @@ function main() {} set content(content: ((ResourceStr | undefined) | undefined)) get content(): ((ResourceStr | undefined) | undefined) - set __backing_content(__backing_content: (IPropRefDecoratedVariable<(ResourceStr | undefined)> | undefined)) + set __backing_content(__backing_content: (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined)) - get __backing_content(): (IPropRefDecoratedVariable<(ResourceStr | undefined)> | undefined) + get __backing_content(): (IPropDecoratedVariable<(ResourceStr | undefined)> | undefined) set isLoading(isLoading: (boolean | undefined)) get isLoading(): (boolean | undefined) - set __backing_isLoading(__backing_isLoading: (IPropRefDecoratedVariable | undefined)) + set __backing_isLoading(__backing_isLoading: (IPropDecoratedVariable | undefined)) - get __backing_isLoading(): (IPropRefDecoratedVariable | undefined) + get __backing_isLoading(): (IPropDecoratedVariable | undefined) set code(code: (number | undefined)) get code(): (number | undefined) diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts index a127c2a52199abfbc10b53f8621f618800549f03..f726ecd68a44c8f99046d705f51033d4ed5ce62f 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/decorator-no-type.test.ts @@ -52,6 +52,8 @@ import { memo as memo } from "arkui.stateManagement.runtime"; import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; import { OBSERVE as OBSERVE } from "arkui.stateManagement.decorator"; @@ -78,13 +80,12 @@ import { CustomComponent as CustomComponent } from "arkui.component.customCompon import { Component as Component, ComponentV2 as ComponentV2, CustomDialog as CustomDialog } from "@ohos.arkui.component"; -import { State as State, Provide as Provide, Event as Event, Local as Local, Param as Param } from "@ohos.arkui.stateManagement"; +import { State as State, Prop as Prop, Provide as Provide, Event as Event, Local as Local, Param as Param } from "@ohos.arkui.stateManagement"; import { Provider as Provider, Consumer as Consumer, Once as Once, Observed as Observed, ObservedV2 as ObservedV2, Trace as Trace, Track as Track } from "@ohos.arkui.stateManagement"; function main() {} - class Per { public num: number; @@ -256,6 +257,8 @@ final class StateType extends BaseEnum { public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_stateVar1 = STATE_MGMT_FACTORY.makeState(this, "stateVar1", (((({let gensym___213853607 = initializers; (((gensym___213853607) == (null)) ? undefined : gensym___213853607.stateVar1)})) ?? (new Per(6))) as Per)); + this.__backing_stateVar2 = STATE_MGMT_FACTORY.makeProp>(this, "stateVar2", (((({let gensym___113574154 = initializers; + (((gensym___113574154) == (null)) ? undefined : gensym___113574154.stateVar2)})) ?? (new Array(3, 6, 8))) as Array)); this.__backing_stateVar3 = STATE_MGMT_FACTORY.makeProvide(this, "stateVar3", "stateVar3", (((({let gensym___120612294 = initializers; (((gensym___120612294) == (null)) ? undefined : gensym___120612294.stateVar3)})) ?? (StateType.TYPE3)) as StateType), false); this.__backing_stateVar8 = ((({let gensym___188075012 = initializers; @@ -270,7 +273,12 @@ final class StateType extends BaseEnum { (((gensym___159362057) == (null)) ? undefined : gensym___159362057.stateVar11115)})) ?? (null)) as null)); } - public __updateStruct(initializers: (__Options_Parent | undefined)): void {} + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___130780487 = initializers; + (((gensym___130780487) == (null)) ? undefined : gensym___130780487.stateVar2)})) !== (undefined))) { + this.__backing_stateVar2!.update((initializers!.stateVar2 as Array)); + } + } private __backing_stateVar1?: IStateDecoratedVariable; @@ -282,6 +290,16 @@ final class StateType extends BaseEnum { this.__backing_stateVar1!.set(value); } + private __backing_stateVar2?: IPropDecoratedVariable>; + + public get stateVar2(): Array { + return this.__backing_stateVar2!.get(); + } + + public set stateVar2(value: Array) { + this.__backing_stateVar2!.set(value); + } + private __backing_stateVar3?: IProvideDecoratedVariable; public get stateVar3(): StateType { @@ -657,6 +675,12 @@ final class StateType extends BaseEnum { set __backing_stateVar1(__backing_stateVar1: (IStateDecoratedVariable | undefined)) get __backing_stateVar1(): (IStateDecoratedVariable | undefined) + set stateVar2(stateVar2: (Any | undefined)) + + get stateVar2(): (Any | undefined) + set __backing_stateVar2(__backing_stateVar2: (IPropDecoratedVariable | undefined)) + + get __backing_stateVar2(): (IPropDecoratedVariable | undefined) set stateVar3(stateVar3: (Any | undefined)) get stateVar3(): (Any | undefined) diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts index 9ca9a9bb7e2ae12ddf9d5349b4669b8538568898..a528a6e1ab307d1ba1f27b020304b16c93b25626 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts @@ -38,7 +38,7 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; @@ -62,7 +62,7 @@ import { CustomComponent as CustomComponent } from "arkui.component.customCompon import { Component as Component, Column as Column, TextInput as TextInput } from "@ohos.arkui.component"; -import { Link as Link, State as State, PropRef as PropRef } from "@ohos.arkui.stateManagement"; +import { Link as Link, State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} @@ -120,8 +120,8 @@ function main() {} }; this.__backing_childText2 = STATE_MGMT_FACTORY.makeState(this, "childText2", ((({let gensym___95513066 = initializers; (((gensym___95513066) == (null)) ? undefined : gensym___95513066.childText2)})) ?? ("sss"))); - this.__backing_childText3 = STATE_MGMT_FACTORY.makePropRef(this, "childText3", (initializers!.childText3 as string)); - this.__backing_childText4 = STATE_MGMT_FACTORY.makePropRef(this, "childText4", ((({let gensym___162028107 = initializers; + this.__backing_childText3 = STATE_MGMT_FACTORY.makeProp(this, "childText3", (initializers!.childText3 as string)); + this.__backing_childText4 = STATE_MGMT_FACTORY.makeProp(this, "childText4", ((({let gensym___162028107 = initializers; (((gensym___162028107) == (null)) ? undefined : gensym___162028107.childText4)})) ?? ("cc"))); } @@ -156,7 +156,7 @@ function main() {} this.__backing_childText2!.set(value); } - private __backing_childText3?: IPropRefDecoratedVariable; + private __backing_childText3?: IPropDecoratedVariable; public get childText3(): string { return this.__backing_childText3!.get(); @@ -166,7 +166,7 @@ function main() {} this.__backing_childText3!.set(value); } - private __backing_childText4?: IPropRefDecoratedVariable; + private __backing_childText4?: IPropDecoratedVariable; public get childText4(): string { return this.__backing_childText4!.get(); @@ -217,15 +217,15 @@ function main() {} set childText3(childText3: (string | undefined)) get childText3(): (string | undefined) - set __backing_childText3(__backing_childText3: (IPropRefDecoratedVariable | undefined)) + set __backing_childText3(__backing_childText3: (IPropDecoratedVariable | undefined)) - get __backing_childText3(): (IPropRefDecoratedVariable | undefined) + get __backing_childText3(): (IPropDecoratedVariable | undefined) set childText4(childText4: (string | undefined)) get childText4(): (string | undefined) - set __backing_childText4(__backing_childText4: (IPropRefDecoratedVariable | undefined)) + set __backing_childText4(__backing_childText4: (IPropDecoratedVariable | undefined)) - get __backing_childText4(): (IPropRefDecoratedVariable | undefined) + get __backing_childText4(): (IPropDecoratedVariable | undefined) } `; diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b58c22be8260da0b7ccc323b62f7aabeb65aee7 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-basic-type.test.ts @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-basic-type.ets'), +]; + +const pluginTester = new PluginTester('test basic type @Prop decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-basic-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Prop as Prop } from "@ohos.arkui.stateManagement"; + +function main() {} + + + +@Component() final struct PropParent extends CustomComponent { + public __initializeStruct(initializers: (__Options_PropParent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? ("propVar1"))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (50))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (true))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (undefined))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? (null))); + } + + public __updateStruct(initializers: (__Options_PropParent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as string)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as number)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as boolean)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as undefined)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as null)); + } + } + + private __backing_propVar1?: IPropDecoratedVariable; + + public get propVar1(): string { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: string) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropDecoratedVariable; + + public get propVar2(): number { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: number) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropDecoratedVariable; + + public get propVar3(): boolean { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: boolean) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropDecoratedVariable; + + public get propVar4(): undefined { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: undefined) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropDecoratedVariable; + + public get propVar5(): null { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: null) { + this.__backing_propVar5!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_PropParent { + set propVar1(propVar1: (string | undefined)) + + get propVar1(): (string | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (number | undefined)) + + get propVar2(): (number | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable | undefined) + set propVar3(propVar3: (boolean | undefined)) + + get propVar3(): (boolean | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (undefined | undefined)) + + get propVar4(): (undefined | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable | undefined) + set propVar5(propVar5: (null | undefined)) + + get propVar5(): (null | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test basic type @Prop decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c619e10ce290e731a8a5a2db4a4ec90274217eac --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/prop-complex-type.test.ts @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { structNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'prop-complex-type.ets'), +]; + +const pluginTester = new PluginTester('test complex type @Prop decorated variables transformation', buildConfig); + +const parsedTransform: Plugins = { + name: 'prop-complex-type', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component } from "@ohos.arkui.component"; + +import { Prop as Prop } from "@ohos.arkui.stateManagement"; + +function main() {} + +class Per { + public num: number; + + public constructor(num: number) { + this.num = num; + } + +} + +final class PropType extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly TYPE1: PropType = new PropType(0, 0); + + public static readonly TYPE2: PropType = new PropType(1, 1); + + public static readonly TYPE3: PropType = new PropType(2, 3); + + private static readonly #NamesArray: String[] = ["TYPE1", "TYPE2", "TYPE3"]; + + private static readonly #ValuesArray: int[] = [0, 1, 3]; + + private static readonly #StringValuesArray: String[] = ["0", "1", "3"]; + + private static readonly #ItemsArray: PropType[] = [PropType.TYPE1, PropType.TYPE2, PropType.TYPE3]; + + public getName(): String { + return PropType.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): PropType { + for (let i = 0;((i) < (PropType.#NamesArray.length));(++i)) { + if (((name) == (PropType.#NamesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum constant PropType.") + (name))); + } + + public static fromValue(value: int): PropType { + for (let i = 0;((i) < (PropType.#ValuesArray.length));(++i)) { + if (((value) == (PropType.#ValuesArray[i]))) { + return PropType.#ItemsArray[i]; + } + } + throw new Error((("No enum PropType with value ") + (value))); + } + + public valueOf(): int { + return PropType.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return PropType.#StringValuesArray[this.#ordinal]; + } + + public static values(): PropType[] { + return PropType.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: PropType): String { + return e.getName(); + } + +} + +@Component() final struct Parent extends CustomComponent { + public __initializeStruct(initializers: (__Options_Parent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_propVar1 = STATE_MGMT_FACTORY.makeProp(this, "propVar1", ((({let gensym___95172135 = initializers; + (((gensym___95172135) == (null)) ? undefined : gensym___95172135.propVar1)})) ?? (new Per(6)))); + this.__backing_propVar2 = STATE_MGMT_FACTORY.makeProp>(this, "propVar2", ((({let gensym___222490386 = initializers; + (((gensym___222490386) == (null)) ? undefined : gensym___222490386.propVar2)})) ?? (new Array(3, 6, 8)))); + this.__backing_propVar3 = STATE_MGMT_FACTORY.makeProp(this, "propVar3", ((({let gensym___201781257 = initializers; + (((gensym___201781257) == (null)) ? undefined : gensym___201781257.propVar3)})) ?? (PropType.TYPE3))); + this.__backing_propVar4 = STATE_MGMT_FACTORY.makeProp>(this, "propVar4", ((({let gensym___22028950 = initializers; + (((gensym___22028950) == (null)) ? undefined : gensym___22028950.propVar4)})) ?? (new Set(new Array("aa", "bb"))))); + this.__backing_propVar5 = STATE_MGMT_FACTORY.makeProp>(this, "propVar5", ((({let gensym___54872258 = initializers; + (((gensym___54872258) == (null)) ? undefined : gensym___54872258.propVar5)})) ?? ([true, false]))); + this.__backing_propVar6 = STATE_MGMT_FACTORY.makeProp>(this, "propVar6", ((({let gensym___128760941 = initializers; + (((gensym___128760941) == (null)) ? undefined : gensym___128760941.propVar6)})) ?? (new Array(new Per(7), new Per(11))))); + this.__backing_propVar7 = STATE_MGMT_FACTORY.makeProp>(this, "propVar7", ((({let gensym___30534085 = initializers; + (((gensym___30534085) == (null)) ? undefined : gensym___30534085.propVar7)})) ?? ([new Per(7), new Per(11)]))); + this.__backing_propVar8 = STATE_MGMT_FACTORY.makeProp<((sr: string)=> void)>(this, "propVar8", ((({let gensym___12471776 = initializers; + (((gensym___12471776) == (null)) ? undefined : gensym___12471776.propVar8)})) ?? (((sr: string) => {})))); + this.__backing_propVar9 = STATE_MGMT_FACTORY.makeProp(this, "propVar9", ((({let gensym___123472108 = initializers; + (((gensym___123472108) == (null)) ? undefined : gensym___123472108.propVar9)})) ?? (new Date("2025-4-23")))); + this.__backing_propVar10 = STATE_MGMT_FACTORY.makeProp>(this, "propVar10", ((({let gensym___147847012 = initializers; + (((gensym___147847012) == (null)) ? undefined : gensym___147847012.propVar10)})) ?? (new Map([[0, new Per(7)], [1, new Per(10)]])))); + this.__backing_propVar11 = STATE_MGMT_FACTORY.makeProp<(string | number)>(this, "propVar11", ((({let gensym___117026760 = initializers; + (((gensym___117026760) == (null)) ? undefined : gensym___117026760.propVar11)})) ?? (0.0))); + this.__backing_propVar12 = STATE_MGMT_FACTORY.makeProp<(Set | Per)>(this, "propVar12", ((({let gensym___220245132 = initializers; + (((gensym___220245132) == (null)) ? undefined : gensym___220245132.propVar12)})) ?? (new Per(6)))); + } + + public __updateStruct(initializers: (__Options_Parent | undefined)): void { + if (((({let gensym___67969738 = initializers; + (((gensym___67969738) == (null)) ? undefined : gensym___67969738.propVar1)})) !== (undefined))) { + this.__backing_propVar1!.update((initializers!.propVar1 as Per)); + } + if (((({let gensym___52350476 = initializers; + (((gensym___52350476) == (null)) ? undefined : gensym___52350476.propVar2)})) !== (undefined))) { + this.__backing_propVar2!.update((initializers!.propVar2 as Array)); + } + if (((({let gensym___103864283 = initializers; + (((gensym___103864283) == (null)) ? undefined : gensym___103864283.propVar3)})) !== (undefined))) { + this.__backing_propVar3!.update((initializers!.propVar3 as PropType)); + } + if (((({let gensym___175155715 = initializers; + (((gensym___175155715) == (null)) ? undefined : gensym___175155715.propVar4)})) !== (undefined))) { + this.__backing_propVar4!.update((initializers!.propVar4 as Set)); + } + if (((({let gensym___134530703 = initializers; + (((gensym___134530703) == (null)) ? undefined : gensym___134530703.propVar5)})) !== (undefined))) { + this.__backing_propVar5!.update((initializers!.propVar5 as Array)); + } + if (((({let gensym___211600890 = initializers; + (((gensym___211600890) == (null)) ? undefined : gensym___211600890.propVar6)})) !== (undefined))) { + this.__backing_propVar6!.update((initializers!.propVar6 as Array)); + } + if (((({let gensym___124229427 = initializers; + (((gensym___124229427) == (null)) ? undefined : gensym___124229427.propVar7)})) !== (undefined))) { + this.__backing_propVar7!.update((initializers!.propVar7 as Array)); + } + if (((({let gensym___248056380 = initializers; + (((gensym___248056380) == (null)) ? undefined : gensym___248056380.propVar8)})) !== (undefined))) { + this.__backing_propVar8!.update((initializers!.propVar8 as ((sr: string)=> void))); + } + if (((({let gensym___55399278 = initializers; + (((gensym___55399278) == (null)) ? undefined : gensym___55399278.propVar9)})) !== (undefined))) { + this.__backing_propVar9!.update((initializers!.propVar9 as Date)); + } + if (((({let gensym___125042885 = initializers; + (((gensym___125042885) == (null)) ? undefined : gensym___125042885.propVar10)})) !== (undefined))) { + this.__backing_propVar10!.update((initializers!.propVar10 as Map)); + } + if (((({let gensym___2015283 = initializers; + (((gensym___2015283) == (null)) ? undefined : gensym___2015283.propVar11)})) !== (undefined))) { + this.__backing_propVar11!.update((initializers!.propVar11 as (string | number))); + } + if (((({let gensym___39009414 = initializers; + (((gensym___39009414) == (null)) ? undefined : gensym___39009414.propVar12)})) !== (undefined))) { + this.__backing_propVar12!.update((initializers!.propVar12 as (Set | Per))); + } + } + + private __backing_propVar1?: IPropDecoratedVariable; + + public get propVar1(): Per { + return this.__backing_propVar1!.get(); + } + + public set propVar1(value: Per) { + this.__backing_propVar1!.set(value); + } + + private __backing_propVar2?: IPropDecoratedVariable>; + + public get propVar2(): Array { + return this.__backing_propVar2!.get(); + } + + public set propVar2(value: Array) { + this.__backing_propVar2!.set(value); + } + + private __backing_propVar3?: IPropDecoratedVariable; + + public get propVar3(): PropType { + return this.__backing_propVar3!.get(); + } + + public set propVar3(value: PropType) { + this.__backing_propVar3!.set(value); + } + + private __backing_propVar4?: IPropDecoratedVariable>; + + public get propVar4(): Set { + return this.__backing_propVar4!.get(); + } + + public set propVar4(value: Set) { + this.__backing_propVar4!.set(value); + } + + private __backing_propVar5?: IPropDecoratedVariable>; + + public get propVar5(): Array { + return this.__backing_propVar5!.get(); + } + + public set propVar5(value: Array) { + this.__backing_propVar5!.set(value); + } + + private __backing_propVar6?: IPropDecoratedVariable>; + + public get propVar6(): Array { + return this.__backing_propVar6!.get(); + } + + public set propVar6(value: Array) { + this.__backing_propVar6!.set(value); + } + + private __backing_propVar7?: IPropDecoratedVariable>; + + public get propVar7(): Array { + return this.__backing_propVar7!.get(); + } + + public set propVar7(value: Array) { + this.__backing_propVar7!.set(value); + } + + private __backing_propVar8?: IPropDecoratedVariable<((sr: string)=> void)>; + + public get propVar8(): ((sr: string)=> void) { + return this.__backing_propVar8!.get(); + } + + public set propVar8(value: ((sr: string)=> void)) { + this.__backing_propVar8!.set(value); + } + + private __backing_propVar9?: IPropDecoratedVariable; + + public get propVar9(): Date { + return this.__backing_propVar9!.get(); + } + + public set propVar9(value: Date) { + this.__backing_propVar9!.set(value); + } + + private __backing_propVar10?: IPropDecoratedVariable>; + + public get propVar10(): Map { + return this.__backing_propVar10!.get(); + } + + public set propVar10(value: Map) { + this.__backing_propVar10!.set(value); + } + + private __backing_propVar11?: IPropDecoratedVariable<(string | number)>; + + public get propVar11(): (string | number) { + return this.__backing_propVar11!.get(); + } + + public set propVar11(value: (string | number)) { + this.__backing_propVar11!.set(value); + } + + private __backing_propVar12?: IPropDecoratedVariable<(Set | Per)>; + + public get propVar12(): (Set | Per) { + return this.__backing_propVar12!.get(); + } + + public set propVar12(value: (Set | Per)) { + this.__backing_propVar12!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Component() export interface __Options_Parent { + set propVar1(propVar1: (Per | undefined)) + + get propVar1(): (Per | undefined) + set __backing_propVar1(__backing_propVar1: (IPropDecoratedVariable | undefined)) + + get __backing_propVar1(): (IPropDecoratedVariable | undefined) + set propVar2(propVar2: (Array | undefined)) + + get propVar2(): (Array | undefined) + set __backing_propVar2(__backing_propVar2: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar2(): (IPropDecoratedVariable> | undefined) + set propVar3(propVar3: (PropType | undefined)) + + get propVar3(): (PropType | undefined) + set __backing_propVar3(__backing_propVar3: (IPropDecoratedVariable | undefined)) + + get __backing_propVar3(): (IPropDecoratedVariable | undefined) + set propVar4(propVar4: (Set | undefined)) + + get propVar4(): (Set | undefined) + set __backing_propVar4(__backing_propVar4: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar4(): (IPropDecoratedVariable> | undefined) + set propVar5(propVar5: (Array | undefined)) + + get propVar5(): (Array | undefined) + set __backing_propVar5(__backing_propVar5: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar5(): (IPropDecoratedVariable> | undefined) + set propVar6(propVar6: (Array | undefined)) + + get propVar6(): (Array | undefined) + set __backing_propVar6(__backing_propVar6: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar6(): (IPropDecoratedVariable> | undefined) + set propVar7(propVar7: (Array | undefined)) + + get propVar7(): (Array | undefined) + set __backing_propVar7(__backing_propVar7: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar7(): (IPropDecoratedVariable> | undefined) + set propVar8(propVar8: (((sr: string)=> void) | undefined)) + + get propVar8(): (((sr: string)=> void) | undefined) + set __backing_propVar8(__backing_propVar8: (IPropDecoratedVariable<((sr: string)=> void)> | undefined)) + + get __backing_propVar8(): (IPropDecoratedVariable<((sr: string)=> void)> | undefined) + set propVar9(propVar9: (Date | undefined)) + + get propVar9(): (Date | undefined) + set __backing_propVar9(__backing_propVar9: (IPropDecoratedVariable | undefined)) + + get __backing_propVar9(): (IPropDecoratedVariable | undefined) + set propVar10(propVar10: (Map | undefined)) + + get propVar10(): (Map | undefined) + set __backing_propVar10(__backing_propVar10: (IPropDecoratedVariable> | undefined)) + + get __backing_propVar10(): (IPropDecoratedVariable> | undefined) + set propVar11(propVar11: ((string | number) | undefined)) + + get propVar11(): ((string | number) | undefined) + set __backing_propVar11(__backing_propVar11: (IPropDecoratedVariable<(string | number)> | undefined)) + + get __backing_propVar11(): (IPropDecoratedVariable<(string | number)> | undefined) + set propVar12(propVar12: ((Set | Per) | undefined)) + + get propVar12(): ((Set | Per) | undefined) + set __backing_propVar12(__backing_propVar12: (IPropDecoratedVariable<(Set | Per)> | undefined)) + + get __backing_propVar12(): (IPropDecoratedVariable<(Set | Per)> | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test complex type @Prop decorated variables transformation', + [parsedTransform, structNoRecheck, recheck], + { + 'checked:struct-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b5648f03ebd5db587239485d5399bcb1883a020 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const BUILDER_LAMBDA_DIR_PATH: string = 'decorators/prop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, BUILDER_LAMBDA_DIR_PATH, 'state-to-prop.ets'), +]; + +const pluginTester = new PluginTester('test @Prop decorated variables passing', buildConfig); + +const parsedTransform: Plugins = { + name: 'state-to-prop', + parsed: uiTransform().parsed +}; + +const expectedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; + +import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; + +import { ButtonImpl as ButtonImpl } from "arkui.component.button"; + +import { ConditionScope as ConditionScope } from "arkui.component.builder"; + +import { ConditionBranch as ConditionBranch } from "arkui.component.builder"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { TextImpl as TextImpl } from "arkui.component.text"; + +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Text as Text, Button as Button, Column as Column, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { Prop as Prop, State as State } from "@ohos.arkui.stateManagement"; + +function main() {} + +@Component() final struct CountDownComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_CountDownComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_count = STATE_MGMT_FACTORY.makeProp(this, "count", ((({let gensym___58710805 = initializers; + (((gensym___58710805) == (null)) ? undefined : gensym___58710805.count)})) ?? (0))); + this.__backing_costOfOneAttempt = ((({let gensym___88948111 = initializers; + (((gensym___88948111) == (null)) ? undefined : gensym___88948111.costOfOneAttempt)})) ?? (1)); + } + + public __updateStruct(initializers: (__Options_CountDownComponent | undefined)): void { + if (((({let gensym___188547633 = initializers; + (((gensym___188547633) == (null)) ? undefined : gensym___188547633.count)})) !== (undefined))) { + this.__backing_count!.update((initializers!.count as number)); + } + } + + private __backing_count?: IPropDecoratedVariable; + + public get count(): number { + return this.__backing_count!.get(); + } + + public set count(value: number) { + this.__backing_count!.set(value); + } + + private __backing_costOfOneAttempt?: number; + + public get costOfOneAttempt(): number { + return (this.__backing_costOfOneAttempt as number); + } + + public set costOfOneAttempt(value: number) { + this.__backing_costOfOneAttempt = value; + } + + @memo() public build() { + ColumnImpl(@memo() ((instance: ColumnAttribute): void => { + instance.setColumnOptions(undefined).applyAttributesFinish(); + return; + }), @memo() (() => { + ConditionScope(@memo() (() => { + if (((this.count) > (0))) { + ConditionBranch(@memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions((((("You have") + (this.count))) + ("Nuggets left")), undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } else { + ConditionBranch(@memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions("Game over!", undefined).applyAttributesFinish(); + return; + }), undefined); + })); + } + })); + ButtonImpl(@memo() ((instance: ButtonAttribute): void => { + instance.setButtonOptions("Try again", undefined).onClick(((e: ClickEvent) => { + this.count -= this.costOfOneAttempt; + })).applyAttributesFinish(); + return; + }), undefined); + })); + } + + public constructor() {} + +} + +@Component() final struct ParentComponent extends CustomComponent { + public __initializeStruct(initializers: (__Options_ParentComponent | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_countDownStartValue = STATE_MGMT_FACTORY.makeState(this, "countDownStartValue", ((({let gensym___249912438 = initializers; + (((gensym___249912438) == (null)) ? undefined : gensym___249912438.countDownStartValue)})) ?? (10))); + } + + public __updateStruct(initializers: (__Options_ParentComponent | undefined)): void {} + + private __backing_countDownStartValue?: IStateDecoratedVariable; + + public get countDownStartValue(): number { + return this.__backing_countDownStartValue!.get(); + } + + public set countDownStartValue(value: number) { + this.__backing_countDownStartValue!.set(value); + } + + @memo() public build() { + ColumnImpl(@memo() ((instance: ColumnAttribute): void => { + instance.setColumnOptions(undefined).applyAttributesFinish(); + return; + }), @memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions((((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined).applyAttributesFinish(); + return; + }), undefined); + ButtonImpl(@memo() ((instance: ButtonAttribute): void => { + instance.setButtonOptions("+1 - Nuggets in New Game", undefined).onClick(((e: ClickEvent) => { + this.countDownStartValue += 1; + })).applyAttributesFinish(); + return; + }), undefined); + ButtonImpl(@memo() ((instance: ButtonAttribute): void => { + instance.setButtonOptions("-1 - Nuggets in New Game", undefined).onClick(((e: ClickEvent) => { + this.countDownStartValue -= 1; + })).applyAttributesFinish(); + return; + }), undefined); + CountDownComponent._instantiateImpl(undefined, (() => { + return new CountDownComponent(); + }), { + count: this.countDownStartValue, + costOfOneAttempt: 2, + }, undefined, undefined); + })); + } + + public constructor() {} + +} + +@Component() export interface __Options_CountDownComponent { + set count(count: (number | undefined)) + + get count(): (number | undefined) + set __backing_count(__backing_count: (IPropDecoratedVariable | undefined)) + + get __backing_count(): (IPropDecoratedVariable | undefined) + set costOfOneAttempt(costOfOneAttempt: (number | undefined)) + + get costOfOneAttempt(): (number | undefined) + +} + +@Component() export interface __Options_ParentComponent { + set countDownStartValue(countDownStartValue: (number | undefined)) + + get countDownStartValue(): (number | undefined) + set __backing_countDownStartValue(__backing_countDownStartValue: (IStateDecoratedVariable | undefined)) + + get __backing_countDownStartValue(): (IStateDecoratedVariable | undefined) + +} +`; + +function testParsedAndCheckedTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test @Prop decorated variables passing', + [parsedTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testParsedAndCheckedTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts index 60719f9ae98d1cea32cb2642994671bb80d18d1d..a7892cf9f6c32f8652f69f650cce4c6091ccd0ea 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/require/basic-require.test.ts @@ -41,7 +41,7 @@ const expectedParsedScript: string = ` import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; -import { State as State, Require as Require, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; @Component() final struct MyStateSample extends CustomComponent { public hello: string = "hello"; @@ -50,6 +50,7 @@ import { State as State, Require as Require, Provide as Provide, Param as Param @Require() @State() public select0!: number; @Require() @State() public select3?: (number | null); @Require() @State() public select4?: undefined; + @Require() @Prop() public select1!: string; @Require() @Provide({alias:"15"}) public select2!: string[]; @Require() @Provide({alias:"t"}) public select6?: (string[] | undefined | string); @Require() @BuilderParam() public builder!: (()=> void); @@ -80,6 +81,8 @@ import { State as State, Require as Require, Provide as Provide, Param as Param @Require() @State() __backing_select3?: (number | null); select4?: undefined; @Require() @State() __backing_select4?: undefined; + select1?: string; + @Require() @Prop() __backing_select1?: string; select2?: string[]; @Require() @Provide({alias:"15"}) __backing_select2?: string[]; select6?: (string[] | undefined | string); @@ -98,13 +101,14 @@ import { State as State, Require as Require, Provide as Provide, Param as Param const expectedCheckedScript: string = ` import { IParamDecoratedVariable as IParamDecoratedVariable } from "arkui.stateManagement.decorator"; import { IProvideDecoratedVariable as IProvideDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, ComponentV2 as ComponentV2, BuilderParam as BuilderParam } from "@ohos.arkui.component"; -import { State as State, Require as Require, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; +import { State as State, Require as Require, Prop as Prop, Provide as Provide, Param as Param } from "@ohos.arkui.stateManagement"; function main() {} @@ -119,13 +123,19 @@ function main() {} this.__backing_select0 = STATE_MGMT_FACTORY.makeState(this, "select0", (initializers!.select0 as number)); this.__backing_select3 = STATE_MGMT_FACTORY.makeState<(number | null)>(this, "select3", (initializers!.select3 as (number | null))); this.__backing_select4 = STATE_MGMT_FACTORY.makeState(this, "select4", (initializers!.select4 as undefined)); + this.__backing_select1 = STATE_MGMT_FACTORY.makeProp(this, "select1", (initializers!.select1 as string)); this.__backing_select2 = STATE_MGMT_FACTORY.makeProvide>(this, "select2", "15", (initializers!.select2 as Array), false); this.__backing_select6 = STATE_MGMT_FACTORY.makeProvide<(Array | undefined | string)>(this, "select6", "t", (initializers!.select6 as (Array | undefined | string)), false); this.__backing_builder = ((((({let gensym___57081607 = initializers; (((gensym___57081607) == (null)) ? undefined : gensym___57081607.builder)})) ?? (content))) ?? (undefined)) } - public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void { + if (((({let gensym___171969630 = initializers; + (((gensym___171969630) == (null)) ? undefined : gensym___171969630.select1)})) !== (undefined))) { + this.__backing_select1!.update((initializers!.select1 as string)); + } + } private __backing_hello?: string; @@ -187,6 +197,16 @@ function main() {} this.__backing_select4!.set(value); } + private __backing_select1?: IPropDecoratedVariable; + + public get select1(): string { + return this.__backing_select1!.get(); + } + + public set select1(value: string) { + this.__backing_select1!.set(value); + } + private __backing_select2?: IProvideDecoratedVariable>; public get select2(): Array { @@ -278,6 +298,12 @@ function main() {} @Require() set __backing_select4(__backing_select4: (IStateDecoratedVariable | undefined)) @Require() get __backing_select4(): (IStateDecoratedVariable | undefined) + set select1(select1: (string | undefined)) + + get select1(): (string | undefined) + @Require() set __backing_select1(__backing_select1: (IPropDecoratedVariable | undefined)) + + @Require() get __backing_select1(): (IPropDecoratedVariable | undefined) set select2(select2: (Array | undefined)) get select2(): (Array | undefined) diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts index 16665023cc9d6fc660efbb15d14771e18ed820d9..1414b3d22ba530e1e74cd4b4f29d6e62fb120e87 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-basic.test.ts @@ -38,21 +38,25 @@ const reusableTransform: Plugins = { const pluginTester = new PluginTester('test basic reusable', buildConfig); const expectedScript: string = ` +import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; + import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; -import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { memo as memo } from "arkui.stateManagement.runtime"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; import { Component as Component, Reusable as Reusable } from "@ohos.arkui.component"; -import { State as State } from "@ohos.arkui.stateManagement"; +import { State as State, Prop as Prop } from "@ohos.arkui.stateManagement"; function main() {} + @Component() final struct MyStateSample extends CustomComponent { public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void {} @@ -62,7 +66,7 @@ function main() {} Child._instantiateImpl(undefined, (() => { return new Child(); }), { - num1: 5, + num: 5, }, "Child", undefined); } @@ -72,19 +76,37 @@ function main() {} @Component() @Reusable() final struct Child extends CustomComponent { public __initializeStruct(initializers: (__Options_Child | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_num = STATE_MGMT_FACTORY.makeProp(this, "num", ((({let gensym___83257243 = initializers; + (((gensym___83257243) == (null)) ? undefined : gensym___83257243.num)})) ?? (1))); this.__backing_num1 = STATE_MGMT_FACTORY.makeState(this, "num1", ((({let gensym___24398512 = initializers; (((gensym___24398512) == (null)) ? undefined : gensym___24398512.num1)})) ?? (2))); } - public __updateStruct(initializers: (__Options_Child | undefined)): void {} + public __updateStruct(initializers: (__Options_Child | undefined)): void { + if (((({let gensym___108716469 = initializers; + (((gensym___108716469) == (null)) ? undefined : gensym___108716469.num)})) !== (undefined))) { + this.__backing_num!.update((initializers!.num as number)); + } + } public override constructor __toRecord(params: Object): Record { const paramsCasted = (params as __Options_Child); return { + "num": ((paramsCasted.num) ?? (new Object())), "num1": ((paramsCasted.num1) ?? (new Object())), }; } + private __backing_num?: IPropDecoratedVariable; + + public get num(): number { + return this.__backing_num!.get(); + } + + public set num(value: number) { + this.__backing_num!.set(value); + } + private __backing_num1?: IStateDecoratedVariable; public get num1(): number { @@ -106,6 +128,12 @@ function main() {} } @Component() @Reusable() export interface __Options_Child { + set num(num: (number | undefined)) + + get num(): (number | undefined) + set __backing_num(__backing_num: (IPropDecoratedVariable | undefined)) + + get __backing_num(): (IPropDecoratedVariable | undefined) set num1(num1: (number | undefined)) get num1(): (number | undefined) diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..65c703a7b4e47277d1c18d3674029d9d24f09a44 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-appstorage.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop with appstorage', buildConfig); + +const expectedScript: string = ` +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { TextAttribute as TextAttribute } from "arkui.component.text"; + +import { TextImpl as TextImpl } from "arkui.component.text"; + +import { ColumnImpl as ColumnImpl } from "arkui.component.column"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry, Column as Column, Text as Text, ClickEvent as ClickEvent } from "@ohos.arkui.component"; + +import { StorageProp as StorageProp, AppStorage as AppStorage } from "@ohos.arkui.stateManagement"; + +function main() {} + +AppStorage.setOrCreate("PropA", 47); +AppStorage.setOrCreate("PropB", new Data(50)); + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-appstorage", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-appstorage", + integratedHsp: "false", + } as NavInterface)); + +class Data { + public code: number; + + public constructor(code: number) { + this.code = code; + } + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct Index extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_Index | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_storageProp = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropA", "storageProp", 1) + this.__backing_storagePropObject = STATE_MGMT_FACTORY.makeStoragePropRef(this, "PropB", "storagePropObject", new Data(1)) + } + + public __updateStruct(initializers: (__Options_Index | undefined)): void {} + + private __backing_storageProp?: IStoragePropRefDecoratedVariable; + + public get storageProp(): number { + return this.__backing_storageProp!.get(); + } + + public set storageProp(value: number) { + this.__backing_storageProp!.set(value); + } + + private __backing_storagePropObject?: IStoragePropRefDecoratedVariable; + + public get storagePropObject(): Data { + return this.__backing_storagePropObject!.get(); + } + + public set storagePropObject(value: Data) { + this.__backing_storagePropObject!.set(value); + } + + @memo() public build() { + ColumnImpl(@memo() ((instance: ColumnAttribute): void => { + instance.setColumnOptions(undefined).applyAttributesFinish(); + return; + }), @memo() (() => { + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions(\`From AppStorage \${this.storageProp}\`, undefined).onClick(((e: ClickEvent) => { + this.storageProp += 1; + })).applyAttributesFinish(); + return; + }), undefined); + TextImpl(@memo() ((instance: TextAttribute): void => { + instance.setTextOptions(\`From AppStorage \${this.storagePropObject.code}\`, undefined).onClick(((e: ClickEvent) => { + this.storagePropObject.code += 1; + })).applyAttributesFinish(); + return; + }), undefined); + })); + } + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_Index { + set storageProp(storageProp: (number | undefined)) + + get storageProp(): (number | undefined) + set __backing_storageProp(__backing_storageProp: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_storageProp(): (IStoragePropRefDecoratedVariable | undefined) + set storagePropObject(storagePropObject: (Data | undefined)) + + get storagePropObject(): (Data | undefined) + set __backing_storagePropObject(__backing_storagePropObject: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_storagePropObject(): (IStoragePropRefDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + Index._instantiateImpl(undefined, (() => { + return new Index(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop with appstorage', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..65aea99c9f412cc1e5eaf02e11704cacf08b8ae6 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-complex-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop complex type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { NavInterface as NavInterface } from "arkui.UserView"; +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; +import { EntryPoint as EntryPoint } from "arkui.UserView"; +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; +import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-complex-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-complex-type", + integratedHsp: "false", + } as NavInterface)); + +class Person { + public name: string = ""; + + public constructor(name: string) {} + +} + +final class Status extends BaseEnum { + private readonly #ordinal: int; + + private static () {} + + public constructor(ordinal: int, value: int) { + super(value); + this.#ordinal = ordinal; + } + + public static readonly Success: Status = new Status(0, 200); + + public static readonly NotFound: Status = new Status(1, 404); + + public static readonly ServerError: Status = new Status(2, 500); + + private static readonly #NamesArray: String[] = ["Success", "NotFound", "ServerError"]; + + private static readonly #ValuesArray: int[] = [200, 404, 500]; + + private static readonly #StringValuesArray: String[] = ["200", "404", "500"]; + + private static readonly #ItemsArray: Status[] = [Status.Success, Status.NotFound, Status.ServerError]; + + public getName(): String { + return Status.#NamesArray[this.#ordinal]; + } + + public static getValueOf(name: String): Status { + for (let i = 0;((i) < (Status.#NamesArray.length));(++i)) { + if (((name) == (Status.#NamesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum constant Status.") + (name))); + } + + public static fromValue(value: int): Status { + for (let i = 0;((i) < (Status.#ValuesArray.length));(++i)) { + if (((value) == (Status.#ValuesArray[i]))) { + return Status.#ItemsArray[i]; + } + } + throw new Error((("No enum Status with value ") + (value))); + } + + public valueOf(): int { + return Status.#ValuesArray[this.#ordinal]; + } + + public toString(): String { + return Status.#StringValuesArray[this.#ordinal]; + } + + public static values(): Status[] { + return Status.#ItemsArray; + } + + public getOrdinal(): int { + return this.#ordinal; + } + + public static $_get(e: Status): String { + return e.getName(); + } + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_arrayB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop1", "arrayB", [1, 2, 3]) + this.__backing_objectB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "objectB", {}) + this.__backing_dateB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "dateB", new Date("2021-09-09")) + this.__backing_setB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop4", "setB", new Set()) + this.__backing_mapB = STATE_MGMT_FACTORY.makeStoragePropRef>(this, "Prop5", "mapB", new Map()) + this.__backing_classB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop7", "classB", new Person("Kevin")) + this.__backing_enumB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop8", "enumB", Status.NotFound) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_arrayB?: IStoragePropRefDecoratedVariable>; + + public get arrayB(): Array { + return this.__backing_arrayB!.get(); + } + + public set arrayB(value: Array) { + this.__backing_arrayB!.set(value); + } + + private __backing_objectB?: IStoragePropRefDecoratedVariable; + + public get objectB(): Object { + return this.__backing_objectB!.get(); + } + + public set objectB(value: Object) { + this.__backing_objectB!.set(value); + } + + private __backing_dateB?: IStoragePropRefDecoratedVariable; + + public get dateB(): Date { + return this.__backing_dateB!.get(); + } + + public set dateB(value: Date) { + this.__backing_dateB!.set(value); + } + + private __backing_setB?: IStoragePropRefDecoratedVariable>; + + public get setB(): Set { + return this.__backing_setB!.get(); + } + + public set setB(value: Set) { + this.__backing_setB!.set(value); + } + + private __backing_mapB?: IStoragePropRefDecoratedVariable>; + + public get mapB(): Map { + return this.__backing_mapB!.get(); + } + + public set mapB(value: Map) { + this.__backing_mapB!.set(value); + } + + private __backing_classB?: IStoragePropRefDecoratedVariable; + + public get classB(): Person { + return this.__backing_classB!.get(); + } + + public set classB(value: Person) { + this.__backing_classB!.set(value); + } + + private __backing_enumB?: IStoragePropRefDecoratedVariable; + + public get enumB(): Status { + return this.__backing_enumB!.get(); + } + + public set enumB(value: Status) { + this.__backing_enumB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set arrayB(arrayB: (Array | undefined)) + + get arrayB(): (Array | undefined) + set __backing_arrayB(__backing_arrayB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_arrayB(): (IStoragePropRefDecoratedVariable> | undefined) + set objectB(objectB: (Object | undefined)) + + get objectB(): (Object | undefined) + set __backing_objectB(__backing_objectB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_objectB(): (IStoragePropRefDecoratedVariable | undefined) + set dateB(dateB: (Date | undefined)) + + get dateB(): (Date | undefined) + set __backing_dateB(__backing_dateB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_dateB(): (IStoragePropRefDecoratedVariable | undefined) + set setB(setB: (Set | undefined)) + + get setB(): (Set | undefined) + set __backing_setB(__backing_setB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_setB(): (IStoragePropRefDecoratedVariable> | undefined) + set mapB(mapB: (Map | undefined)) + + get mapB(): (Map | undefined) + set __backing_mapB(__backing_mapB: (IStoragePropRefDecoratedVariable> | undefined)) + + get __backing_mapB(): (IStoragePropRefDecoratedVariable> | undefined) + set classB(classB: (Person | undefined)) + + get classB(): (Person | undefined) + set __backing_classB(__backing_classB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_classB(): (IStoragePropRefDecoratedVariable | undefined) + set enumB(enumB: (Status | undefined)) + + get enumB(): (Status | undefined) + set __backing_enumB(__backing_enumB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_enumB(): (IStoragePropRefDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop complex type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..660a51361e77b1c811511ebda2b255bc1a66e897 --- /dev/null +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as path from 'path'; +import { PluginTester } from '../../../../utils/plugin-tester'; +import { mockBuildConfig } from '../../../../utils/artkts-config'; +import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../../utils/path-config'; +import { parseDumpSrc } from '../../../../utils/parse-string'; +import { uiNoRecheck, recheck } from '../../../../utils/plugins'; +import { BuildConfig, PluginTestContext } from '../../../../utils/shared-types'; +import { uiTransform } from '../../../../../ui-plugins'; +import { Plugins } from '../../../../../common/plugin-context'; + +const STORAGEPROP_DIR_PATH: string = 'decorators/storageprop'; + +const buildConfig: BuildConfig = mockBuildConfig(); +buildConfig.compileFiles = [ + path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, STORAGEPROP_DIR_PATH, 'storageprop-primitive-type.ets'), +]; + +const storagePropTransform: Plugins = { + name: 'storageprop', + parsed: uiTransform().parsed, +} + +const pluginTester = new PluginTester('test storageprop primitive type transform', buildConfig); + +const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; + +import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; + +import { IStoragePropRefDecoratedVariable as IStoragePropRefDecoratedVariable } from "arkui.stateManagement.decorator"; + +import { NavInterface as NavInterface } from "arkui.UserView"; + +import { PageLifeCycle as PageLifeCycle } from "arkui.component.customComponent"; + +import { EntryPoint as EntryPoint } from "arkui.UserView"; + + +import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + +import { Component as Component, Entry as Entry } from "@ohos.arkui.component"; + +import { StorageProp as StorageProp } from "@ohos.arkui.stateManagement"; + +function main() {} + +__EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ + bundleName: "com.example.mock", + moduleName: "entry", + pagePath: "../../../decorators/storageprop/storageprop-primitive-type", + pageFullPath: "test/demo/mock/decorators/storageprop/storageprop-primitive-type", + integratedHsp: "false", + } as NavInterface)); + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() final struct MyStateSample extends CustomComponent implements PageLifeCycle { + public __initializeStruct(initializers: (__Options_MyStateSample | undefined), @memo() content: ((()=> void) | undefined)): void { + this.__backing_numB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop1", "numB", 43) + this.__backing_stringB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop2", "stringB", "BB") + this.__backing_booleanB = STATE_MGMT_FACTORY.makeStoragePropRef(this, "Prop3", "booleanB", false) + } + + public __updateStruct(initializers: (__Options_MyStateSample | undefined)): void {} + + private __backing_numB?: IStoragePropRefDecoratedVariable; + + public get numB(): number { + return this.__backing_numB!.get(); + } + + public set numB(value: number) { + this.__backing_numB!.set(value); + } + + private __backing_stringB?: IStoragePropRefDecoratedVariable; + + public get stringB(): string { + return this.__backing_stringB!.get(); + } + + public set stringB(value: string) { + this.__backing_stringB!.set(value); + } + + private __backing_booleanB?: IStoragePropRefDecoratedVariable; + + public get booleanB(): boolean { + return this.__backing_booleanB!.get(); + } + + public set booleanB(value: boolean) { + this.__backing_booleanB!.set(value); + } + + @memo() public build() {} + + public constructor() {} + +} + +@Entry({useSharedStorage:false,storage:"",routeName:""}) @Component() export interface __Options_MyStateSample { + set numB(numB: (number | undefined)) + + get numB(): (number | undefined) + set __backing_numB(__backing_numB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_numB(): (IStoragePropRefDecoratedVariable | undefined) + set stringB(stringB: (string | undefined)) + + get stringB(): (string | undefined) + set __backing_stringB(__backing_stringB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_stringB(): (IStoragePropRefDecoratedVariable | undefined) + set booleanB(booleanB: (boolean | undefined)) + + get booleanB(): (boolean | undefined) + set __backing_booleanB(__backing_booleanB: (IStoragePropRefDecoratedVariable | undefined)) + + get __backing_booleanB(): (IStoragePropRefDecoratedVariable | undefined) + +} + +class __EntryWrapper extends EntryPoint { + @memo() public entry(): void { + MyStateSample._instantiateImpl(undefined, (() => { + return new MyStateSample(); + }), undefined, undefined, undefined); + } + + public constructor() {} + +} +`; + +function testStoragePropTransformer(this: PluginTestContext): void { + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); +} + +pluginTester.run( + 'test storageprop primitive type transform', + [storagePropTransform, uiNoRecheck, recheck], + { + 'checked:ui-no-recheck': [testStoragePropTransformer], + }, + { + stopAfter: 'checked', + } +); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts index f7a95cbcc5c7a5cecaedc9871c45d0667031631c..21063804fc7813bdbe6d375e338ba4a8363c37e4 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts @@ -52,7 +52,7 @@ import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorato import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; -import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; @@ -87,7 +87,7 @@ import { CustomComponent as CustomComponent } from "arkui.component.customCompon import { Component as Component, Entry as Entry, Column as Column } from "@ohos.arkui.component"; -import { State as State, PropRef as PropRef, StorageLink as StorageLink, StoragePropRef as StoragePropRef, Link as Link, Watch as Watch, ObjectLink as ObjectLink, Observed as Observed, Track as Track, Provide as Provide, Consume as Consume } from "@ohos.arkui.stateManagement"; +import { State as State, Prop as Prop, StorageLink as StorageLink, StorageProp as StorageProp, Link as Link, Watch as Watch, ObjectLink as ObjectLink, Observed as Observed, Track as Track, Provide as Provide, Consume as Consume } from "@ohos.arkui.stateManagement"; function main() {} @@ -155,7 +155,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ (((gensym___76198660) == (null)) ? undefined : gensym___76198660.statevar)})) ?? ("Hello World")), ((_: string): void => { this.stateOnChange(_); })); - this.__backing_propvar = STATE_MGMT_FACTORY.makePropRef(this, "propvar", ((({let gensym___241486692 = initializers; + this.__backing_propvar = STATE_MGMT_FACTORY.makeProp(this, "propvar", ((({let gensym___241486692 = initializers; (((gensym___241486692) == (null)) ? undefined : gensym___241486692.propvar)})) ?? ("Hello World")), ((_: string): void => { this.propOnChange(_); })); @@ -202,7 +202,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ this.__backing_statevar!.set(value); } - private __backing_propvar?: IPropRefDecoratedVariable; + private __backing_propvar?: IPropDecoratedVariable; public get propvar(): string { return this.__backing_propvar!.get(); @@ -326,9 +326,9 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ set propvar(propvar: (string | undefined)) get propvar(): (string | undefined) - @Watch({value:"propOnChange"}) set __backing_propvar(__backing_propvar: (IPropRefDecoratedVariable | undefined)) + @Watch({value:"propOnChange"}) set __backing_propvar(__backing_propvar: (IPropDecoratedVariable | undefined)) - @Watch({value:"propOnChange"}) get __backing_propvar(): (IPropRefDecoratedVariable | undefined) + @Watch({value:"propOnChange"}) get __backing_propvar(): (IPropDecoratedVariable | undefined) @__Link_intrinsic() set linkvar(linkvar: (string | undefined)) @__Link_intrinsic() get linkvar(): (string | undefined) diff --git a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts index 587cef36d97be0055d9d46e455bb2297d53ab7c0..4ec11d1c7fc7de617fea6aa6b8705ea010f7d757 100644 --- a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts @@ -46,7 +46,7 @@ import { EntryPoint as EntryPoint } from "arkui.UserView"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { PropRef as PropRef, Column as Column, Entry as Entry } from "@kit.ArkUI"; +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; @@ -59,7 +59,7 @@ import hilog from "@ohos.hilog"; @Entry() @Component() final struct A extends CustomComponent implements PageLifeCycle { @State() public a: string = "str"; - @PropRef() public b!: string; + @Prop() public b!: string; public build() { Column(){ @@ -76,7 +76,7 @@ import hilog from "@ohos.hilog"; a?: string; @State() __backing_a?: string; b?: string; - @PropRef() __backing_b?: string; + @Prop() __backing_b?: string; } @@ -99,7 +99,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ const expectedCheckedScript: string = ` -import { IPropRefDecoratedVariable as IPropRefDecoratedVariable } from "arkui.stateManagement.decorator"; +import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; @@ -127,7 +127,7 @@ import { EntryPoint as EntryPoint } from "arkui.UserView"; import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; -import { PropRef as PropRef, Column as Column, Entry as Entry } from "@kit.ArkUI"; +import { Prop as Prop, Column as Column, Entry as Entry } from "@kit.ArkUI"; import { Text as Text, Component as Component, ClickEvent as ClickEvent } from "@ohos.arkui.component"; @@ -151,7 +151,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ public __initializeStruct(initializers: (__Options_A | undefined), @memo() content: ((()=> void) | undefined)): void { this.__backing_a = STATE_MGMT_FACTORY.makeState(this, "a", ((({let gensym___94024326 = initializers; (((gensym___94024326) == (null)) ? undefined : gensym___94024326.a)})) ?? ("str"))); - this.__backing_b = STATE_MGMT_FACTORY.makePropRef(this, "b", (initializers!.b as string)); + this.__backing_b = STATE_MGMT_FACTORY.makeProp(this, "b", (initializers!.b as string)); } public __updateStruct(initializers: (__Options_A | undefined)): void { @@ -171,7 +171,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ this.__backing_a!.set(value); } - private __backing_b?: IPropRefDecoratedVariable; + private __backing_b?: IPropDecoratedVariable; public get b(): string { return this.__backing_b!.get(); @@ -211,9 +211,9 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ set b(b: (string | undefined)) get b(): (string | undefined) - set __backing_b(__backing_b: (IPropRefDecoratedVariable | undefined)) + set __backing_b(__backing_b: (IPropDecoratedVariable | undefined)) - get __backing_b(): (IPropRefDecoratedVariable | undefined) + get __backing_b(): (IPropDecoratedVariable | undefined) } diff --git a/arkui-plugins/ui-plugins/property-translators/index.ts b/arkui-plugins/ui-plugins/property-translators/index.ts index ff8a89025895f1b719f6321aa71f315984bc1c8e..914006824c8ec1c4ffbcb63e1103f23147d53c3b 100644 --- a/arkui-plugins/ui-plugins/property-translators/index.ts +++ b/arkui-plugins/ui-plugins/property-translators/index.ts @@ -19,13 +19,16 @@ import { DecoratorNames } from '../../common/predefines'; import { InterfacePropertyTranslator, MethodTranslator, PropertyTranslator } from './base'; import { hasDecorator } from './utils'; import { StateInterfaceTranslator, StateTranslator } from './state'; +import { PropInterfaceTranslator, PropTranslator } from './prop'; import { StorageLinkInterfaceTranslator, StorageLinkTranslator } from './storagelink'; import { LocalStorageLinkInterfaceTranslator, LocalStorageLinkTranslator } from './localstoragelink'; import { LinkInterfaceTranslator, LinkTranslator } from './link'; import { ObjectLinkInterfaceTranslator, ObjectLinkTranslator } from './objectlink'; +import { LocalStoragePropInterfaceTranslator, LocalStoragePropTranslator } from './localstorageprop'; import { RegularInterfaceTranslator, RegularPropertyTranslator } from './regularProperty'; import { StaticPropertyTranslator } from './staticProperty'; import { CustomComponentInfo } from '../utils'; +import { StoragePropInterfaceTranslator, StoragePropTranslator } from './storageProp'; import { ConsumeInterfaceTranslator, ConsumeTranslator } from './consume'; import { ProvideInterfaceTranslator, ProvideTranslator } from './provide'; import { BuilderParamInterfaceTranslator, BuilderParamTranslator } from './builderParam'; @@ -100,12 +103,21 @@ export function classifyV1Property( if (hasDecorator(property, DecoratorNames.OBJECT_LINK)) { return new ObjectLinkTranslator({ property, structInfo }); } + if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP)) { + return new LocalStoragePropTranslator({ property, structInfo }); + } if (hasDecorator(property, DecoratorNames.LOCAL_STORAGE_PROP_REF)) { return new LocalStoragePropRefTranslator({ property, structInfo }); } + if (hasDecorator(property, DecoratorNames.STORAGE_PROP)) { + return new StoragePropTranslator({ property, structInfo }); + } if (hasDecorator(property, DecoratorNames.STORAGE_PROP_REF)) { return new StoragePropRefTranslator({ property, structInfo }); } + if (hasDecorator(property, DecoratorNames.PROP)) { + return new PropTranslator({ property, structInfo }); + } if (hasDecorator(property, DecoratorNames.PROP_REF)) { return new PropRefTranslator({ property, structInfo }); } @@ -171,6 +183,9 @@ export function classifyV1PropertyInInterface(property: arkts.AstNode): Interfac if (LinkInterfaceTranslator.canBeTranslated(property)) { return new LinkInterfaceTranslator({ property }); } + if (PropInterfaceTranslator.canBeTranslated(property)) { + return new PropInterfaceTranslator({ property }); + } if (PropRefInterfaceTranslator.canBeTranslated(property)) { return new PropRefInterfaceTranslator({ property }); } @@ -180,6 +195,9 @@ export function classifyV1PropertyInInterface(property: arkts.AstNode): Interfac if (ConsumeInterfaceTranslator.canBeTranslated(property)) { return new ConsumeInterfaceTranslator({ property }); } + if (StoragePropInterfaceTranslator.canBeTranslated(property)) { + return new StoragePropInterfaceTranslator({ property }); + } if (StorageLinkInterfaceTranslator.canBeTranslated(property)) { return new StorageLinkInterfaceTranslator({ property }); } @@ -192,6 +210,9 @@ export function classifyV1PropertyInInterface(property: arkts.AstNode): Interfac if (BuilderParamInterfaceTranslator.canBeTranslated(property)) { return new BuilderParamInterfaceTranslator({ property }); } + if (LocalStoragePropInterfaceTranslator.canBeTranslated(property)) { + return new LocalStoragePropInterfaceTranslator({ property }); + } if (LocalStorageLinkInterfaceTranslator.canBeTranslated(property)) { return new LocalStorageLinkInterfaceTranslator({ property }); } diff --git a/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b5840e62f24d9e7e365605ac92fd2656b23ebfb --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/localstorageprop.ts @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, StateManagementTypes } from '../../common/predefines'; +import { collectStateManagementTypeImport, generateToRecord, hasDecorator } from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +function getLocalStorageporpValueStr(node: arkts.AstNode): string | undefined { + if (!arkts.isClassProperty(node) || !node.value) return undefined; + + return arkts.isStringLiteral(node.value) ? node.value.str : undefined; +} +function getLocalStorageporpAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { + const isLocalStorageporpAnnotation: boolean = + !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.LOCAL_STORAGE_PROP; + + if (isLocalStorageporpAnnotation && anno.properties.length === 1) { + return getLocalStorageporpValueStr(anno.properties.at(0)!); + } + return undefined; +} + +function getLocalStorageporpValueInAnnotation(node: arkts.ClassProperty): string | undefined { + const annotations: readonly arkts.AnnotationUsage[] = node.annotations; + + for (let i = 0; i < annotations.length; i++) { + const anno: arkts.AnnotationUsage = annotations[i]; + const str: string | undefined = getLocalStorageporpAnnotationValue(anno); + if (!!str) { + return str; + } + } + + return undefined; +} + +export class LocalStoragePropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.SYNCED_PROPERTY, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + + const member = arkts.factory.createTSNonNullExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + ); + const thisValue: arkts.MemberExpression = arkts.factory.createMemberExpression( + member, + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisValue); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisValue); + return [field, getter, setter]; + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); + if (!localStorageporpValueStr) { + throw new Error('LocalStorageProp required only one value!!'); + } + const insideMember = arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier('_entry_local_storage_'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + const binaryItem = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(StateManagementTypes.STORAGE_LINK_STATE), + this.propertyType ? [this.propertyType] : [], + [ + insideMember, + arkts.factory.createStringLiteral(localStorageporpValueStr), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ] + ); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); + const call = arkts.factory.createCallExpression( + arkts.factory.createIdentifier(StateManagementTypes.PROP_STATE), + this.propertyType ? [this.propertyType] : [], + [ + arkts.factory.createMemberExpression( + binaryItem, + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ] + ); + collectStateManagementTypeImport(StateManagementTypes.PROP_STATE); + return arkts.factory.createAssignmentExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + call + ); + } + + generateUpdateStruct(newName: string, originalName: string): arkts.AstNode { + const localStorageporpValueStr: string | undefined = getLocalStorageporpValueInAnnotation(this.property); + if (!localStorageporpValueStr) { + throw new Error('StorageLink required only one value!!'); + } + const StorageLinkStateValue = factory.createStorageLinkStateValue(this.property, localStorageporpValueStr); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_LINK_STATE); + const test = arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + const consequent = arkts.BlockStatement.createBlockStatement([ + arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(test), + arkts.factory.createIdentifier('update'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [StorageLinkStateValue] + ) + ), + ]); + return arkts.factory.createExpressionStatement(arkts.factory.createIfStatement(test, consequent)); + } +} + +export class LocalStoragePropInterfaceTranslator< + T extends InterfacePropertyTypes +> extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.LOCAL_STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param method expecting getter with `@LocalStorageProp` and a setter with `@LocalStorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.LOCAL_STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `SyncedProperty | undefined`. + * + * @param property expecting property with `@LocalStorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.LOCAL_STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/prop.ts b/arkui-plugins/ui-plugins/property-translators/prop.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4e6fbcb1038f592abc98f3a3f301b488a9451d4 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/prop.ts @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { CustomComponentNames } from '../utils'; +import { + collectStateManagementTypeImport, + createGetter, + createSetter2, + generateGetOrSetCall, + generateThisBacking, + generateToRecord, + hasDecorator, +} from './utils'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class PropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const mutableThis: arkts.Expression = generateThisBacking(newName); + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + const updateStruct: arkts.AstNode = this.generateUpdateStruct(mutableThis, originalName); + PropertyCache.getInstance().collectUpdateStruct(this.structInfo.name, [updateStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field: arkts.ClassProperty = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.PROP_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const args: arkts.Expression[] = [ + arkts.factory.create1StringLiteral(originalName), + factory.generateInitializeValue(this.property, this.propertyType, originalName), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.PROP_DECORATED); + const assign: arkts.AssignmentExpression = arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall(StateManagementTypes.MAKE_PROP, this.propertyType, args, true) + ); + return arkts.factory.createExpressionStatement(assign); + } + + generateUpdateStruct(mutableThis: arkts.Expression, originalName: string): arkts.AstNode { + const member: arkts.MemberExpression = arkts.factory.createMemberExpression( + arkts.factory.createTSNonNullExpression(mutableThis), + arkts.factory.createIdentifier(StateManagementTypes.UPDATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ); + return factory.createIfInUpdateStruct(originalName, member, [ + arkts.factory.createTSAsExpression( + factory.createNonNullOrOptionalMemberExpression( + CustomComponentNames.COMPONENT_INITIALIZERS_NAME, + originalName, + false, + true + ), + this.propertyType, + false + ), + ]); + } +} + +export class PropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@Prop` and a setter with `@Prop` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `PropDecoratedVariable | undefined`. + * + * @param property expecting property with `@Prop`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/storageProp.ts b/arkui-plugins/ui-plugins/property-translators/storageProp.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4b0978d2f40629895d1f76d9d88aae656a74086 --- /dev/null +++ b/arkui-plugins/ui-plugins/property-translators/storageProp.ts @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; + +import { backingField, expectName } from '../../common/arkts-utils'; +import { DecoratorNames, GetSetTypes, StateManagementTypes } from '../../common/predefines'; +import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; +import { GetterSetter, InitializerConstructor } from './types'; +import { + generateToRecord, + createGetter, + createSetter2, + generateThisBacking, + generateGetOrSetCall, + collectStateManagementTypeImport, + hasDecorator, + getValueInAnnotation, +} from './utils'; +import { factory } from './factory'; +import { PropertyCache } from './cache/propertyCache'; + +export class StoragePropTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { + translateMember(): arkts.AstNode[] { + const originalName: string = expectName(this.property.key); + const newName: string = backingField(originalName); + + this.cacheTranslatedInitializer(newName, originalName); + return this.translateWithoutInitializer(newName, originalName); + } + + cacheTranslatedInitializer(newName: string, originalName: string): void { + const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); + PropertyCache.getInstance().collectInitializeStruct(this.structInfo.name, [initializeStruct]); + if (!!this.structInfo.annotations?.reusable) { + const toRecord = generateToRecord(newName, originalName); + PropertyCache.getInstance().collectToRecord(this.structInfo.name, [toRecord]); + } + } + + generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { + const storagePropValueStr: string | undefined = getValueInAnnotation( + this.property, + DecoratorNames.STORAGE_PROP + ); + if (!storagePropValueStr) { + throw new Error('StorageProp required only one value!!'); + } + + const args: arkts.Expression[] = [ + arkts.factory.createStringLiteral(storagePropValueStr), + arkts.factory.create1StringLiteral(originalName), + this.property.value ?? arkts.factory.createUndefinedLiteral(), + ]; + factory.judgeIfAddWatchFunc(args, this.property); + collectStateManagementTypeImport(StateManagementTypes.STORAGE_PROP_REF_DECORATED); + + return arkts.factory.createAssignmentExpression( + generateThisBacking(newName), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + factory.generateStateMgmtFactoryCall( + StateManagementTypes.MAKE_STORAGE_PROP_REF, + this.propertyType, + args, + true + ) + ); + } + + translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const field = factory.createOptionalClassProperty( + newName, + this.property, + StateManagementTypes.STORAGE_PROP_REF_DECORATED, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + ); + const thisValue: arkts.Expression = generateThisBacking(newName, false, true); + const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, GetSetTypes.GET); + const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( + generateGetOrSetCall(thisValue, GetSetTypes.SET) + ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, this.propertyType, thisGet); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, this.propertyType, thisSet); + return [field, getter, setter]; + } + + translateGetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + returnValue: arkts.Expression + ): arkts.MethodDefinition { + return createGetter(originalName, typeAnnotation, returnValue); + } + + translateSetter( + originalName: string, + typeAnnotation: arkts.TypeNode | undefined, + statement: arkts.AstNode + ): arkts.MethodDefinition { + return createSetter2(originalName, typeAnnotation, statement); + } +} + +export class StoragePropInterfaceTranslator extends InterfacePropertyTranslator { + translateProperty(): T { + if (arkts.isMethodDefinition(this.property)) { + this.modified = true; + return this.updateStateMethodInInterface(this.property) as T; + } else if (arkts.isClassProperty(this.property)) { + this.modified = true; + return this.updateStatePropertyInInterface(this.property) as T; + } + return this.property; + } + + static canBeTranslated(node: arkts.AstNode): node is InterfacePropertyTypes { + if (arkts.isMethodDefinition(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } else if (arkts.isClassProperty(node) && hasDecorator(node, DecoratorNames.STORAGE_PROP)) { + return true; + } + return false; + } + + /** + * Wrap getter's return type and setter's param type (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param method expecting getter with `@StorageProp` and a setter with `@StorageProp` in the overloads. + */ + private updateStateMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { + return factory.wrapStateManagementTypeToMethodInInterface(method, DecoratorNames.STORAGE_PROP); + } + + /** + * Wrap to the type of the property (expecting an union type with `T` and `undefined`) + * to `StoragePropDecoratedVariable | undefined`. + * + * @param property expecting property with `@StorageProp`. + */ + private updateStatePropertyInInterface(property: arkts.ClassProperty): arkts.ClassProperty { + return factory.wrapStateManagementTypeToPropertyInInterface(property, DecoratorNames.STORAGE_PROP); + } +} diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index 186a353bcb528ba02f6fc86199a908403bd5d3f3..8e4415efe359cd19db4c6b23481e129063e3c8c6 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -125,6 +125,7 @@ export function needDefiniteOrOptionalModifier(st: arkts.ClassProperty): boolean hasDecoratorName(st, DecoratorNames.LINK) || hasDecoratorName(st, DecoratorNames.CONSUME) || hasDecoratorName(st, DecoratorNames.OBJECT_LINK) || + (hasDecoratorName(st, DecoratorNames.PROP) && !st.value) || (hasDecoratorName(st, DecoratorNames.PROP_REF) && !st.value) || (hasDecoratorName(st, DecoratorNames.PARAM) && !st.value) || (hasDecoratorName(st, DecoratorNames.EVENT) && !st.value) ||